function [fg, fg_unclassified]=dtiFindMoriTracts(dt6File, outFile, fgFile, Atlas, showFig, saveQuench, saveMrDiffusion, useJhuFa, useInterhemisphericSplit, useRoiBasedApproach)
%Categorizes fibers based on Mori white matter atlas.
%
%  dtiFindMoriTracts(dt6File, [outFile=fullfile(fileparts(dt6File), 'fibers', 'MoriGroups.mat')], fgFile,...
%      [Atlas='MNI_JHU_tracts_prob.nii.gz'], [showFig=false],[saveQuench=false],...
%      [saveMrDiffusion=false], [useJhuFa=false],...
%      [useInterhemisphericSplit=true], [useRoiBasedApproach=true]);
%
% By default whole brain tractography is performed and then
% Mori-classified. Otherwise, provide a file name fgFile for a fiber group
% if willing to Mori-classify an existing fiber group.
%
% Input parameters:
% fgFile                   - A file with previously tracked elsewhere fibers
%                          to be categorized.
% Atlas                    - probabilistic atlas defining probabilities for
%                          each voxel to be passed by a fiber within each of
%                          atlas fiber groups. We usually use Mori atlas
%                          supplied with fsl: MNI_JHU_tracts_prob.nii.gz.
%                          This atlas is not symmetric. For a "symmetrified"
%                          atlas use 'MNI_JHU_tracts_prob_Symmetric.nii.gz'
%                          but we strongly recommend using the original
%                          atlas.
% useInterhemisphericSplit - cut fibers crossing between hemispheres with a
%                          midsaggital plane below z=-10. This is to get
%                          rid of
% useRoiBasedApproach      - use the approach describing in Zhang (2008)
%                           Neuroimage 42. For each of the 20 Mori Groups 2
%                           critical ROIs are computed by spatially
%                           transforming ROIs provided in
%                           templates/MNI_JHU_tracts_ROIs. A fiber becomes
%                           a candidate to being labeled as a part of a
%                           given Mori group if this fiber "passes through"
%                           both critical ROIs for that Mori group. Our
%                           modification of Zhang (2008) approach: In case
%                           a single fiber is a candidate for >1 Mori
%                           group, respective cumulative probabilities are
%                           computed with probabilistic Mori atlas, then
%                           compared. useRoiBasedApproach can take the
%                           following values: (1) 'false' (to not use the
%                           approach); (2) 'true'(use the approach; the
%                           minimal distance from a fiber to ROI to count
%                           as "a fiber  is crossing the ROI" minDist=2mm);
%                           (3) a scalar value for minDist in mm; (4) a
%                           vector with the first value being minDist and
%                           the second value being a flag 1/0 for whether
%                           ROIs should be recomputed (and overwritten) for
%                           this subject (1, by default). This is useful
%                           because sometimes if you  are rerunning
%                           dtiFindMoriTracts with different parameters,
%                           you do not need to recompute ROIs. E.g., to
%                           avoid recomputing  ROIs and use minDist of 4mm
%                           one would pass [useRoiBasedApproach=[4 0]];
%
% Output parameters:
% fg              - fibers structure containing all fibers assigned to
%                   one of Mori Groups. Respective group labeles are stored
%                   in fg.subgroups field.
% fg_unclassified - fiber structure containing the rest of the (not Mori) fibers.
%
% Example:
%    dt6File = '/biac3/wandell4/data/reading_longitude/dti_y1234/at040918/dti06trilinrt/dt6.mat';
%    fg = dtiFindMoriTracts(dt6File);
%
% See also: dtiSplitInterhemisphericFibers.m
%
% Usage Notes:
%    Text explaining the usage of this code for publication can be found at
%    the end of this code along with references.
%
% (c) Stanford University, Vistalab

% HISTORY:
% 2008.10.08 RFD wrote it. 2009.01.08 EIR added an option of Mori-categorizing
%            a provided FG (do not supply a fgFile parameter, or supply []
%            if want whole brain tractography performed)
% 2009.01.09 RFD removed the feature that discarded fibers that scored too
%            high on more than one atlas group. This was causing too many
%            good fibers to be removed, especially for groups 19 & 20
%            (arcuate).
% 2009.01.30 EIR modified to write out Mori labels as fg.subgroup
% 2009.02.01 EIR added an option to use symmetrified Mori atlas
% 2009.05.31 EIR modified to output fghandle in addition to fg
% 2009.07.21 EIR modified to output unclassified fibers
% 2009.08.28 EIR added interhemispheric fiber split below ACPC and removed
%                fghandle output (because the parent FG is modified to add
%                split fibers.
% 2009.09.27 ER  added useRoiBasedApproach option which implements Zhang
%                (2008) Neuroimage 42 method.
% 2009.10.2 ER:  no longer need to fit Quench state file labels to 8 grp
%                limit.

%% Check INPUTS

if ~exist('showFig', 'var')|| isempty(showFig)
    showFig= false;
end
if ~exist('saveQuench', 'var')|| isempty(saveQuench)
    saveQuench = false;
end
if ~exist('saveMrDiffusion', 'var')|| isempty(saveMrDiffusion)
    saveMrDiffusion= false;
end
if ~exist('useJhuFa', 'var')|| isempty(useJhuFa)
    useJhuFa = false; %JhuFa should never be used
end
if ~exist('useInterhemisphericSplit', 'var')|| isempty(useInterhemisphericSplit)
    useInterhemisphericSplit=true;
end
if ~exist('useRoiBasedApproach', 'var')|| isempty(useRoiBasedApproach)
    useRoiBasedApproach=true;
end

if useRoiBasedApproach==false
    recomputeROIs = false;
elseif length(useRoiBasedApproach)<2
    recomputeROIs=1;
else
    recomputeROIs=useRoiBasedApproach(2);
end

if recomputeROIs
    display('dtiFindMoriTracts: You chose to recompute ROIs');
end

% E.g., to avoid recomputing  ROIs and use minDist of 4mm one would pass [useRoiBasedApproach=[4 0]];
if isnumeric(useRoiBasedApproach)
    minDist = useRoiBasedApproach(1);
    useRoiBasedApproach = 'true';
else
    minDist=2; %.89;
end
display(['Fibers that get as close to the ROIs as ' num2str(minDist) 'mm will become candidates for the Mori Groups']);

if(~exist('Atlas','var') || isempty(Atlas))
    % Default scenario: use original Mori Atlas
    Atlas='MNI_JHU_tracts_prob.nii.gz';
end

if(~exist('fgFile','var') || isempty(fgFile))
    % Default scenario: perform whole brain tractogrpahy
    wholeBrainFlag=1;
else
    wholeBrainFlag=0;
end

if(~exist('outFile','var') || isempty(outFile))
    bd = fileparts(dt6File);
    if(isempty(bd)), bd = pwd; end
    outFile = fullfile(bd,'fibers', 'MoriGroups.mat');
end

%%

% Load a dt6 file
dt = dtiLoadDt6(dt6File);

tdir = fullfile(fileparts(which('mrDiffusion.m')), 'templates');
spm_defaults; global defaults; params = defaults.normalise.estimate;
if(useJhuFa)
    template = fullfile(tdir,'MNI_JHU_FA.nii.gz');
    alignIm = dtiComputeFA(dt.dt6);
    params.cutoff = 19;
    params.reg = 0.09;
else
    % Spatially normalize it with the MNI (ICBM) template
    template = fullfile(tdir,'MNI_JHU_T2.nii.gz');
    alignIm = mrAnatHistogramClip(double(dt.b0),0.3,0.99);
end

[sn, Vtemplate, invDef] = mrAnatComputeSpmSpatialNorm(alignIm, dt.xformToAcpc, template, params);

% check the normalization
mm = diag(chol(Vtemplate.mat(1:3,1:3)'*Vtemplate.mat(1:3,1:3)))';
bb = mrAnatXformCoords(Vtemplate.mat,[1 1 1; Vtemplate.dim]);
alignIm_sn = mrAnatResliceSpm(alignIm, sn, bb, [2 2 2], [1 1 1 0 0 0], 0);
tIm = mrAnatResliceSpm(double(Vtemplate.dat), inv(Vtemplate.mat), bb, [2 2 2], [1 1 1 0 0 0], 0);
im(:,:,:,1) = uint8(tIm);
im(:,:,:,2) = uint8(round(clip(alignIm_sn)*255));
im(:,:,:,3) = im(:,:,:,2);

if(showFig)

    showMontage(im);
else
    if(~exist(fileparts(outFile),'dir')), mkdir(fileparts(outFile)); end
    imwrite(makeMontage(im),[outFile(1:end-4) '_snCheck.png']);
end

% Load the Mori atlas maps and the corresponding label files
% ldir = fileparts(which('dtiGetBrainlabel.m'));
moriTracts = niftiRead(fullfile(tdir, Atlas));
% 15 is a subregion of 19 and 16 a subregion of 20. To better separate them,
% we subtract 19 from 15 and 20 from 16.
moriTracts.data(:,:,:,15) = moriTracts.data(:,:,:,15)-moriTracts.data(:,:,:,19);
moriTracts.data(:,:,:,16) = moriTracts.data(:,:,:,16)-moriTracts.data(:,:,:,20);

labels = readTab(fullfile(tdir,'MNI_JHU_tracts_prob.txt'),',',false);
labels = labels(1:20,2);
% If you wanted to inverse-normalize the maps to this subject's brain:
% invDef.outMat = moriTracts.qto_ijk;
% bb = mrAnatXformCoords(dt.xformToAcpc,[1 1 1; size(dt.b0)]);
% tprob = mrAnatResliceSpm(tprob, invDef, bb, dt.mmPerVoxel, [1 1 1 0 0 0]);

if wholeBrainFlag
    % Track all white matter fibers in the native subject space. We do this by
    % seeding all voxels with high FA (>0.3).
    faThresh = 0.30;
    opts.stepSizeMm = 1;
    opts.faThresh = .2; %0.15
    opts.lengthThreshMm = [50 250];
    opts.angleThresh = 50;
    opts.wPuncture = 0.2;
    opts.whichAlgorithm = 1;
    opts.whichInterp = 1;
    opts.seedVoxelOffsets = [0.25 0.75];
    opts.offsetJitter = 0.1;
    fa = dtiComputeFA(dt.dt6);
    fa(fa>1) = 1; fa(fa<0) = 0;
    roiAll = dtiNewRoi('all');
    mask = dtiCleanImageMask(fa>=faThresh);
    [x,y,z] = ind2sub(size(mask), find(mask));
    clear mask fa;
    roiAll.coords = mrAnatXformCoords(dt.xformToAcpc, [x,y,z]);
    clear x y z;
    fg = dtiFiberTrack(dt.dt6, roiAll.coords, dt.mmPerVoxel, dt.xformToAcpc, 'wholeBrain', opts);
    clear roiAll
    % wholeBrainFGFile=fullfile(fileparts(dt6File), 'fibers', 'all.mat');
    % dtiWriteFiberGroup(fg, wholeBrainFGFile);
    % fghandle.parent=wholeBrainFGFile;
else
    % get fg from a file
    fg = fgRead(fgFile);
    %fg = dtiLoadFiberGroup(fgFile);
    %fghandle.parent=fgFile;
end

%Cut the fibers below acpc, to disentangle CST& ATL crossing at the pons
%level.
if useInterhemisphericSplit
    fgname=fg.name;
    [fg]=dtiSplitInterhemisphericFibers(fg, dt, -10);
    fg.name=fgname; %To avoid "bilaterally split" extra long comment in the fg name.
end

%Because we know that we are looking for major Mori tracts, we should
%keep only 5 points or longer fibers (otherwise spline in contrack
%crashes).  This is important for Blue matter procedures planned, but wont hurt otherwise (for Mori detection) either.
if sum(cellfun(@length, fg.fibers)<5)~=0
    if isfield(fg, 'subgroup')&&~isempty(fg.subgroup)
        fg.subgroup(cellfun(@length, fg.fibers)<5)=[];
    end
    if isfield(fg, 'seeds')&&~isempty(fg.seeds)
        fg.seeds(cellfun(@length, fg.fibers)<5, :)=[];
    end
    fg.fibers(cellfun(@length, fg.fibers)<5)=[];
    fprintf('dtiFindMoriTracts: Removing %s fibers with 5 points or less \n', num2str(sum(cellfun(@length, fg.fibers)<5)));
end

% Warp the fibers in 'fg' to the MNI standard space:
fg_sn = dtiXformFiberCoords(fg, invDef);

% moriTracts.data is a an XxYxZx20 array contianing the 20 Mori probability
% atlases (range is 0-100 where 100 represents p(1)).
sz = size(moriTracts.data);

% fg_sn fiber coords are in MNI space- now convert them to atlas space by
% applying the affine xform from the atlas NIFTI header. SInce the atlas is
% already in MNI space, this transform will just account for any
% translation and scale differences between the atlas maps and the MNI
% template used to compute our sn.
fgCoords = mrAnatXformCoords(moriTracts.qto_ijk, horzcat(fg_sn.fibers{:}));
clear fg_sn;   % what we need from fg_sn is now stored in fgCoords
fgLen = cellfun('size',fg.fibers,2);

% Now loop over the 20 atlases and get the atlas probability score for each
% fiber point. We collapse the scores across all pints in a fiber by taking
% the mean. Below, we will use these 20 mean scores to categorize the fibers.
% TO DO: consider doing something more sophisticated than taking the mean.
fp = zeros(sz(4),numel(fg.fibers));
for(ii=1:sz(4))
    % Get the Mori atlas score for each point in the fibers using
    % trilinear interpolation.
    p = myCinterp3(double(moriTracts.data(:,:,:,ii))/100, sz([1,2]), sz(3), fgCoords(:,[2,1,3]));
    % The previous line interpolated one giant array with all fiber points
    % concatenated. The next loop will separate the coordinates back into
    % fibers and take the mean score for the points within each fiber.
    fiberCoord = 1;
    for(jj=1:numel(fg.fibers))
        fp(ii,jj) = nanmean(p([fiberCoord:fiberCoord+fgLen(jj)-1]));
        fiberCoord = fiberCoord+fgLen(jj);
    end
end
clear p fgCoords;

if useRoiBasedApproach
    %Warp Mori ROIs to individual space; collect candidates for each fiber
    %group based on protocol of 2 or > ROIs a fiber should travel thru.
    %The following ROIs are saved within
    %trunk/mrDiffusion/templates/MNI_JHU_tracts_ROIs folder and are created using MNI template as
    %described in Wakana et al.(2007) Neuroimage 36 with a single modification:
    %For SLFt Roi2, they recommend drawing the ROI at the AC level, whereas we
    %use a lisce just inferior of CC splenium. The reason for this modification
    %is that Wakana et al. ACPC aligned images appear different from MNI images
    %(the latter we use for defininng ROIs). If defining SLFt-Roi2 on a slice
    %actually at the AC level (althought highly consistently across human
    %raters), many SLFt fibers were not correctly labeled as they extend
    %laterally into temporal lobe just above the aforementioned ROI plane.

    moriRois={'ATR_roi1_L.nii.gz',  'ATR_roi2_L.nii.gz'; 'ATR_roi1_R.nii.gz', 'ATR_roi2_R.nii.gz'; ...
        'CST_roi1_L.nii.gz', 'CST_roi2_L.nii.gz'; 'CST_roi1_R.nii.gz',  'CST_roi2_R.nii.gz'; ...
        'CGC_roi1_L.nii.gz', 'CGC_roi2_L.nii.gz'; 'CGC_roi1_R.nii.gz', 'CGC_roi2_R.nii.gz'; ...
        'HCC_roi1_L.nii.gz', 'HCC_roi2_L.nii.gz'; 'HCC_roi1_R.nii.gz', 'HCC_roi2_R.nii.gz';...
        'FP_R.nii.gz', 'FP_L.nii.gz'; ...
        'FA_L.nii.gz', 'FA_R.nii.gz'; ...
        'IFO_roi1_L.nii.gz', 'IFO_roi2_L.nii.gz'; 'IFO_roi2_R.nii.gz', 'IFO_roi1_R.nii.gz'; ...
        'ILF_roi1_L.nii.gz', 'ILF_roi2_L.nii.gz'; 'ILF_roi1_R.nii.gz', 'ILF_roi2_R.nii.gz'; ...
        'SLF_roi1_L.nii.gz', 'SLF_roi2_L.nii.gz'; 'SLF_roi1_R.nii.gz', 'SLF_roi2_R.nii.gz'; ...
        'UNC_roi1_L.nii.gz', 'UNC_roi2_L.nii.gz'; 'UNC_roi1_R.nii.gz', 'UNC_roi2_R.nii.gz'; ...
        'SLF_roi1_L.nii.gz', 'SLFt_roi2_L.nii.gz'; 'SLF_roi1_R.nii.gz', 'SLFt_roi2_R.nii.gz'};

    midSaggitalRoi = dtiRoiMakePlane([0, dt.bb(1, 2), dt.bb(1, 3); 0 , dt.bb(2, 2) , dt.bb(2, 3)], 'midsaggital', 'g');

    keep1 = zeros(length(fg.fibers), size(moriRois, 1)); keep2=zeros(length(fg.fibers), size(moriRois, 1));

    [fgOut,contentiousFibers,InterHemisphericFibers] = dtiIntersectFibersWithRoi([], 'not', [], midSaggitalRoi, fg);
    %NOTICE: ~keep3 (not "keep3") will mark fibers that DO NOT cross midSaggitalRoi.
    keep3 = repmat(InterHemisphericFibers, [1 size(moriRois, 1)]);

    fgCopy=fg; fgCopy.subgroup=[];
    for roiID=1:size(moriRois, 1)

        ROI_img_file=fullfile(tdir, 'MNI_JHU_tracts_ROIs',  [moriRois{roiID, 1}]);
        if recomputeROIs
            [RoiFileName, invDef, roi]=dtiCreateRoiFromMniNifti(dt6File, ROI_img_file, invDef, true);
        else
            RoiFileName=fullfile(fileparts(dt6File), 'ROIs',  [prefix(prefix(ROI_img_file, 'short'), 'short') '.mat']);
            load(RoiFileName);

        end
        [fgOut,contentiousFibers, keep1(:, roiID)] = dtiIntersectFibersWithRoi([], 'and', minDist, roi, fg);
        keepID1=find(keep1(:, roiID));

        ROI_img_file=fullfile(tdir, 'MNI_JHU_tracts_ROIs',  [moriRois{roiID, 2}]);
        if recomputeROIs
            [RoiFileName, invDef, roi]=dtiCreateRoiFromMniNifti(dt6File, ROI_img_file, invDef, true);
        else
            RoiFileName=fullfile(fileparts(dt6File), 'ROIs',  [prefix(prefix(ROI_img_file, 'short'), 'short') '.mat']);
            load(RoiFileName);
        end

        %To speed up the function, we intersect with the second ROI not all the
        %fibers, but only those that passed first ROI.
        fgCopy.fibers=fg.fibers(keepID1(keepID1>0));
        [a,b, keep2given1] = dtiIntersectFibersWithRoi([], 'and', minDist, roi, fgCopy);
        keep2(keepID1(keep2given1), roiID)=true;

    end
    clear fgOut contentiousFibers keepID
    %Note: forceps major and minor should NOT have interhemipsheric fibers
    %excluded
    keep3(:, 9:10)=keep3(:, 9:10).*0;
    fp(~(keep1'&keep2'&~keep3'))=0;
    %Also note: Tracts that cross through slf_t rois should be automatically
    %classified as slf_t, without considering their probs.
    fp(19, (keep1(:, 19)'&keep2(:, 19)'&~keep3(:, 19)'))=max(fp(:));
    fp(20, (keep1(:, 20)'&keep2(:, 20)'&~keep3(:, 20)'))=max(fp(:));

end

% We have a set of atlas scores for each each fiber. To categorize the
% fibers, we will find the atlas with the highest score (using 'sort').
[atlasScore,atlasInd] = sort(fp,1,'descend');
% Eliminate fibers that don't match any of the atlases very well:
unclassified=atlasScore(1,:)==0; %ER 09.15.09 dropped this threshold to zero
goodEnough = atlasScore(1,:)~=0; % RFD 09.01.09: removed "& atlasScore(1,:)>sum(atlasScore(2:end,:),1);"

for ii=1:sz(4)
    curAtlasFibers{ii} = find(atlasInd(1,:)==ii & goodEnough);
    %    if(showFig)
    %        fc = round(mrAnatXformCoords(inv(dt.xformToAcpc), horzcat(fg.fibers{curAtlasFibers{ii}})));
    %        im = dt.b0;
    %        inds = sub2ind(size(dt.b0),fc(:,1),fc(:,2),fc(:,3));
    %        im(inds) = 1;
    %        makeMontage3(im,dt.b0,dt.b0);
    %        set(gcf,'name',labels{ii},'NumberTitle','off');
    %    end
end

% We now have a cell array (curAtlasFibers) that contains 20 arrays, each
% listing the fiber indices for the corresponding atlas group. E.g.,
% curAtlasFibers{3} is a list of indices into fg.fibers that specify the
% fibers belonging to group 3.

%Create a FG for unclassified fibers
fg_unclassified=fg;
fg_unclassified.name=[fg.name ' not Mori Groups'];
fg_unclassified.fibers = fg.fibers(unclassified); %prepare fg output
if ~isempty(fg.seeds)
    fg_unclassified.seeds = fg.seeds(unclassified);
end
fg_unclassified.subgroup=zeros(size(fg.fibers(unclassified)))'+(1+sz(4));
fg_unclassified.subgroupNames(1)=struct('subgroupIndex', 1+sz(4), 'subgroupName', 'NotMori');

% Modify fg.fibers to discard the fibers that didn't make it into any of
% the atlas groups:
fg.name=[fg.name ' Mori Groups'];
%fghandle.name=fg.name;
fg.fibers = fg.fibers([curAtlasFibers{:}]); %prepare fg output
%fghandle.ids=horzcat(curAtlasFibers{:}); %prepare fghandle output

if ~isempty(fg.seeds)
    fg.seeds = fg.seeds([curAtlasFibers{:}],:);
end

% We changed the size of fg.fibers by discarding the uncategorized fibers,
% so we need to create a new array to categorize the fibers. This time we
% make an array with one entry corresponding to each fiber, with integer
% values indicating to which atlas group the corresponding fiber belongs.
fg.subgroup = zeros(1,numel(fg.fibers));
curInd = 1;
for(ii=1:numel(curAtlasFibers))
    fg.subgroup(curInd:curInd+numel(curAtlasFibers{ii})-1) = ii;
    %fghandle.subgroup(curInd:curInd+numel(curAtlasFibers{ii})-1) = ii;
    curInd = curInd+numel(curAtlasFibers{ii});
    %Save labels for the fiber subgroups within the file
    fg.subgroupNames(ii)=struct('subgroupIndex', ii, 'subgroupName', labels(ii));
    %fghandle.subgroupNames(ii)=struct('subgroupIndex', ii, 'subgroupName', labels(ii));
end

% Save in mrDiffusion format:
if (saveMrDiffusion)
    if(~exist(fileparts(outFile),'dir')), mkdir(fileparts(outFile)); end
    dtiWriteFiberGroup( fg, outFile);
end

if(saveQuench)
    if(~exist(fileparts(outFile),'dir')), mkdir(fileparts(outFile)); end
    % Also save a Quench pdb file/state file to show the fiber groups.
    % Should we use dtiWriteFibersPdb?
    dtiWriteFibersPdb(fg,dt.xformToAcpc, [outFile(1:end-4) '.pdb']);
    % merge some of the groups to fit into the Quench 8-group limit:
    fgInds = fg.subgroup;
    dtiQuenchSaveFibersState(fgInds, [outFile(1:end-4) '.qst']);
end

return;

%%

% To run this on a bunch of subjects:
bd = '/biac3/wandell4/data/reading_longitude/dti_y1234';
[dt6Files,subCodes,subDirs,subIn] = findSubjects(fullfile(bd,'*'), 'dti06rt');
subIn = unique(subIn);
doThese = [1:numel(dt6Files)]; % strmatch('pt0',subCodes)';
for(ii=doThese)
    fiberDir = fullfile(fileparts(dt6Files{ii}),'Mori');
    if(~exist(fiberDir,'dir')), mkdir(fiberDir); end
    outBase = fullfile(fiberDir,'MoriGroups');
    if(~exist([outBase '.mat'],'file'))
        fprintf('Processing %s...\n',dt6Files{ii});
        dtiFindMoriTracts(dt6Files{ii},outBase);
    end
end

subs = []; doThese = [];
for(ii=1:numel(subIn))
    cur = strmatch([subIn{ii} '0'],subCodes);
    if(numel(cur)==4)
        subs = [subs cur'];
    end
end
for(ii=subs)
    fiberDir = fullfile(fileparts(dt6Files{ii}),'Mori');
    if(exist(fiberDir,'dir')&&exist(fullfile(fiberDir,'MoriGroups.mat')))
        %disp([subCodes{ii} ' is finished.']);
    else
        disp([subCodes{ii} ' is MISSING.']);
        doThese = [doThese ii];
    end
end

% To analyze a bunch of subjects:
outDir = '/biac3/wandell4/data/reading_longitude/moriGroupAnalysis';
bd = '/biac3/wandell4/data/reading_longitude/dti_y1234';
[dt6Files,subCodes,subDirs,subLetters] = findSubjects(fullfile(bd,'*'), 'dti06rt');
nGroups = 20;
nSubs = numel(dt6Files);
sgVol = zeros(nSubs,nGroups);
for(ii=1:nSubs)
    fname = fullfile(fileparts(dt6Files{ii}),'Mori','MoriGroups.mat');
    fprintf('Processing %s (%d of %d)...\n',fname,ii,numel(dt6Files));
    fg = dtiReadFibers(fname);
    for(jj=1:nGroups)
        sgCoords = fg.fibers(fg.subgroup==jj);
        % crude volume measure:
        sgVol(ii,jj) = size(unique(round(horzcat(sgCoords{:}))','rows'),1);
    end
end

% Get the labels for the 20 groups:
labels = readTab(which('MNI_JHU_tracts_prob.txt'),',',false);
labels = labels(:,2);
save(fullfile(outDir,'moriGroupSum.mat'),'bd','labels','sgVol','dt6Files','subCodes','subLetters');

outDir = '/biac3/wandell4/data/reading_longitude/moriGroupAnalysis';
load(fullfile(outDir,'moriGroupSum.mat'));
nGroups = size(sgVol,2);
nSubs = size(sgVol,1);
mn = mean(sgVol)/1000;
sd = std(sgVol)/1000;
rng = [max(sgVol); min(sgVol)]/1000;
for(ii=1:nGroups)
    fprintf('%50s:\t %0.2fcc (%0.3f, %0.2f-%0.2f)\n',labels{ii},mn(ii),sd(ii),rng(1,ii),rng(2,ii));
end

[bd, colNames, sc, subYr] = dtiGetBehavioralData(subCodes);

yr = 4;
bdVars = {'Passage Comprehension.', 'Rapid Naming.', 'Word attack ss.', 'Phonological Awareness.', 'Calculation.','WISC Full-Scale IQ.','DTI Age.','Sex (1=male)'};
for(ii=1:numel(bdVars))
    for(jj=1:2:nGroups-1)
        if(bdVars{ii}(end)=='.'), bVar = sprintf('%s%d',bdVars{ii},yr);
        else bVar = bdVars{ii}; end
        s = subYr==yr;
        x = bd(s,strmatch(bVar,colNames,'exact'));
        if(~isempty(x))
            y = sgVol(s,jj+1)-sgVol(s,jj);  % sgVol(s,jj);
            gv = ~isnan(x)&~isnan(y);
            x = x(gv); y = y(gv);
            %figure(34); plot(x,y,'.');
            [p,r,df] = myStatTest(x,y,'r');
            fprintf('%25s vs. %50s:\t r=%+0.3f (p=%0.2g, df=%d)\n',bVar,labels{jj},r,p,df);
        end
    end
end

%% TEXT FOR PUBLICATION

% The fiber tracts from the whole brain tractography were automatically
% classified into twenty fiber structures as defined in the JHU
% white-matter tractography atlas (Wakana et al., 2007) using a modified
% form of the reference ROI approach ( [Wakana et al., 2007] , [Zhang et
% al., 2008] , [Hua et al., 2008] and [Zhang et al., 2010] ). Specifically,
% we manually defined reference ROIs (rROIs) describing two waypoints for
% each of the 20 major white matter tracts described in (Wakana et al.,
% 2007). These ROIs were drawn in MNI space on the ICBM-DTI-81 atlas by two
% experts. The rROIs were warped from MNI space into each individual's
% diffusion space and fibers were retained if they passed through any pair
% of rROIs. Note that some fibers passed through the rROIs for more than
% one major tract. Thus, we applied an additional inclusion criterion by
% warping each fiber to MNI space and measuring the approximate overlap
% between the fiber points and each of the major tracts of the
% probabilistic JHU tractography atlas (Wakana et al., 2007). The fiber was
% classified as representing the JHU tract with which it had the highest
% degree of overlap.
%

%% REFERENCES:
%
% Wakana, Setsu et al. 2007. “Reproducibility of quantitative tractography
% methods applied to cerebral white matter.” NeuroImage 36(3): 630-44.
%
% Zhang, Weihong, Alessandro Olivi, Samuel J Hertig, Peter van Zijl, et
% al. 2008. “Automated fiber tracking of human brain white matter using
% diffusion tensor imaging.” NeuroImage 42(2): 771-7.
%
% Hua, K, J Zhang, S Wakana, H Jiang, et al. 2008. “Tract probability maps
% in stereotaxic spaces: analyses of white matter anatomy and
% tract-specific quantification.” Neuroimage 39(1): 336-347.
%
% Zhang et al., 2010 Y. Zhang, J. Zhang, K. Oishi, A. Faria, H. Jiang, X.
% Li, K. Akhter, P. Rosa-Neto, G. Pike, A. Evans, A. Toga, R. Woods, J.
% Mazziotta, M. Miller, P. van Zijl and S. Mori, Atlas-guided tract
% reconstruction for automated and comprehensive examination of the white
% matter anatomy, . Neuroimage, 52 4 (2010), pp. 1289–1301 (May).


classdef TimeMagnitudePlotter < TimeSomethingPlotter
    % Used to create time-mag plots
    %
    % plotting multiple times will simply replace the existing plot
    %
    % Example:
    %   TimeMagnitudePlotter.plot(catalog,bigCat)
    %   ax = TimeMagnitudePlotter.axes;
    %   TimeMagnitudePlotter.clear();

    %TODO add ability to plot "big" events

    methods
        function obj=TimeMagnitudePlotter()
            obj@TimeSomethingPlotter('Magnitude','');
        end

        function pl=plot(obj,ax,catalog,bigcat)
            % plot plot a time-mag series for this catalog, with symbol sizes representing
            % event size
            % pl = plot(catalog)
            %
            if ~exist('ax','var') || isempty(ax)
                % ax = findobj('Tag','time_mag_axis');
                f=figure('Name','Time-Magnitude Plot','NumberTitle','off', ...
                    'Tag',obj.Tags.Figure);
                addAboutMenuItem();
                obj.ax=axes(f);
            else
                obj.ax=ax;
            end
            if isempty(obj.ax.Tag)
                obj.ax.Tag=obj.Tags.Axes;
            end

            obj.ax.Visible = 'off';

            % plotting from ZERO magnitude is arbitrary, and stemplot becomes unusable if all magnitudes
            % are below zero. Therefore, make sure stems are always going up
            minMag= min(catalog.Magnitude);
            baseValue = min([0 , floor(minMag)]);

            if obj.hasLotsOfEvents(catalog)
                pl=obj.scatter(catalog);
            else
                pl=obj.stem(catalog, 'BaseValue', baseValue);
            end

            obj.prepare_axes(catalog);

            if exist('bigcat','var')
                obj.overlayBigEvents(bigcat);
            end
            obj.ax.Visible = 'on';
        end

    end
end


%%test network
cam1_size=size(test_data_cam1);
cam2_size=size(test_data_cam2);
cam1_feature1=[];
cam1_feature2=[];
cam1_feature3=[];
cam1_score=[];
test_batch_data=zeros(224,224,3,param.test_batch_size,'single');
for m=0:floor(cam1_size(1)/param.test_batch_size)-1
    for n=1:param.test_batch_size
        im_data=imresize((reshape(single(test_data_cam1(m*param.test_batch_size+n,:)),128,64,3)),[224 224]);
        im_data=im_data(:,:,[3,2,1]);
        im_data=permute(im_data,[2,1,3]);
        test_batch_data(:,:,:,n)=im_data;
        test_batch_data(:,:,1,n)=test_batch_data(:,:,1,n)-104;
        test_batch_data(:,:,2,n)=test_batch_data(:,:,2,n)-117;
        test_batch_data(:,:,3,n)=test_batch_data(:,:,3,n)-123;
    end
    net.blobs('data').set_data(test_batch_data);
    net.forward_prefilled;
    cam1_feature1=[cam1_feature1;(squeeze(net.blobs('data1_p_1').get_data))'];
    cam1_feature2=[cam1_feature2;(squeeze(net.blobs('data1_p_2').get_data))'];
    cam1_feature3=[cam1_feature3;(squeeze(net.blobs('data1_p_3').get_data))'];
    cam1_score=[cam1_score;(squeeze(net.blobs('sig').get_data))'];
end
test_batch_data=zeros(224,224,3,param.test_batch_size,'single');
for m=(floor(cam1_size(1)/param.test_batch_size))*param.test_batch_size+1:cam1_size(1)
        count=m-(floor(cam1_size(1)/param.test_batch_size))*param.test_batch_size;
        im_data=imresize((reshape(single(test_data_cam1(m,:)),128,64,3)),[224 224]);
        im_data=im_data(:,:,[3,2,1]);
        im_data=permute(im_data,[2,1,3]);
        test_batch_data(:,:,:,count)=im_data;
        test_batch_data(:,:,1,count)=test_batch_data(:,:,1,count)-104;
        test_batch_data(:,:,2,count)=test_batch_data(:,:,2,count)-117;
        test_batch_data(:,:,3,count)=test_batch_data(:,:,3,count)-123;
end
net.blobs('data').set_data(test_batch_data);
net.forward_prefilled;
index=cam1_size(1)-(floor(cam1_size(1)/param.test_batch_size))*param.test_batch_size;
result1=(squeeze(net.blobs('data1_p_1').get_data))';
result2=(squeeze(net.blobs('data1_p_2').get_data))';
result3=(squeeze(net.blobs('data1_p_3').get_data))';
score=(squeeze(net.blobs('sig').get_data))';
cam1_feature1=[cam1_feature1;result1(1:index,:)];
cam1_feature2=[cam1_feature2;result2(1:index,:)];
cam1_feature3=[cam1_feature3;result3(1:index,:)];
cam1_score=[cam1_score;score(1:index,:)];
prob_feature=[];
name_cam1={};
for m=1:param.train_person_num
    test_label_index=find(label_test_cam1==(m+param.train_person_num));
    a=bsxfun(@times,cam1_feature1(test_label_index,:),cam1_score(test_label_index,1));
    b=bsxfun(@times,cam1_feature2(test_label_index,:),cam1_score(test_label_index,2));
    c=bsxfun(@times,cam1_feature3(test_label_index,:),cam1_score(test_label_index,3));
    prob_feature=[prob_feature;sum(a/sum(cam1_score(test_label_index,1))),sum(b/sum(cam1_score(test_label_index,2))),sum(c/sum(cam1_score(test_label_index,3)))];
%     prob_feature=[prob_feature;sum(a/sum(cam1_score(test_label_index,1)))];
%     prob_feature=[prob_feature;sum(b/sum(cam1_score(test_label_index,2)))];
%     prob_feature=[prob_feature;sum(c/sum(cam1_score(test_label_index,3)))];
name_cam1=[name_cam1,test_image_name_cam1{test_label_index(1)}];
end
%extract feature cam2
cam2_feature1=[];
cam2_feature2=[];
cam2_feature3=[];
cam2_score=[];
test_batch_data=zeros(224,224,3,param.test_batch_size,'single');
for m=0:floor(cam2_size(1)/param.test_batch_size)-1
    for n=1:param.test_batch_size
        im_data=imresize((reshape(single(test_data_cam2(m*param.test_batch_size+n,:)),128,64,3)),[224 224]);
        im_data=im_data(:,:,[3,2,1]);
        im_data=permute(im_data,[2,1,3]);
        test_batch_data(:,:,:,n)=im_data;
        test_batch_data(:,:,1,n)=test_batch_data(:,:,1,n)-104;
        test_batch_data(:,:,2,n)=test_batch_data(:,:,2,n)-117;
        test_batch_data(:,:,3,n)=test_batch_data(:,:,3,n)-123;
    end
    net.blobs('data').set_data(test_batch_data);
    net.forward_prefilled;
    cam2_feature1=[cam2_feature1;(squeeze(net.blobs('data1_p_1').get_data))'];
    cam2_feature2=[cam2_feature2;(squeeze(net.blobs('data1_p_2').get_data))'];
    cam2_feature3=[cam2_feature3;(squeeze(net.blobs('data1_p_3').get_data))'];
    cam2_score=[cam2_score;(squeeze(net.blobs('sig').get_data))'];
end
test_batch_data=zeros(224,224,3,param.test_batch_size,'single');
for m=(floor(cam2_size(1)/param.test_batch_size))*param.test_batch_size+1:cam2_size(1)
        count=m-(floor(cam2_size(1)/param.test_batch_size))*param.test_batch_size;
        im_data=imresize((reshape(single(test_data_cam2(m,:)),128,64,3)),[224 224]);
        im_data=im_data(:,:,[3,2,1]);
        im_data=permute(im_data,[2,1,3]);
        test_batch_data(:,:,:,count)=im_data;
        test_batch_data(:,:,1,count)=test_batch_data(:,:,1,count)-104;
        test_batch_data(:,:,2,count)=test_batch_data(:,:,2,count)-117;
        test_batch_data(:,:,3,count)=test_batch_data(:,:,3,count)-123;
end
net.blobs('data').set_data(test_batch_data);
net.forward_prefilled;
index=cam2_size(1)-(floor(cam2_size(1)/param.test_batch_size))*param.test_batch_size;
result1=(squeeze(net.blobs('data1_p_1').get_data))';
result2=(squeeze(net.blobs('data1_p_2').get_data))';
result3=(squeeze(net.blobs('data1_p_3').get_data))';
score=(squeeze(net.blobs('sig').get_data))';
cam2_feature1=[cam2_feature1;result1(1:index,:)];
cam2_feature2=[cam2_feature2;result2(1:index,:)];
cam2_feature3=[cam2_feature3;result3(1:index,:)];
cam2_score=[cam2_score;score(1:index,:)];
gallery_feature=[];
name_cam2={};
for m=1:param.train_person_num
    test_label_index=find(label_test_cam2==(m+param.train_person_num));
    a=bsxfun(@times,cam2_feature1(test_label_index,:),cam2_score(test_label_index,1));
    b=bsxfun(@times,cam2_feature2(test_label_index,:),cam2_score(test_label_index,2));
    c=bsxfun(@times,cam2_feature3(test_label_index,:),cam2_score(test_label_index,3));
    gallery_feature=[gallery_feature;sum(a/sum(cam2_score(test_label_index,1))),sum(b/sum(cam2_score(test_label_index,2))),sum(c/sum(cam2_score(test_label_index,3)))];
%     gallery_feature=[gallery_feature;sum(a/sum(cam2_score(test_label_index,1)))];
%     gallery_feature=[gallery_feature;sum(b/sum(cam2_score(test_label_index,2)))];
%     gallery_feature=[gallery_feature;sum(c/sum(cam2_score(test_label_index,3)))];
name_cam2=[name_cam2,test_image_name_cam2(test_label_index(1))];
end
% cal cmc
prob_norm=bsxfun(@rdivide,prob_feature,sum(abs(prob_feature).^2,2).^(1/2));
gallery_norm=bsxfun(@rdivide,gallery_feature,sum(abs(gallery_feature).^2,2).^(1/2));
% [~,it]=sort(prob_norm*gallery_norm',2,'descend');
score_matrix=prob_norm*gallery_norm';
rank1_hit=0;
rank5_hit=0;
rank10_hit=0;
rank20_hit=0;
problen=size(prob_feature,1);
for m=1:problen
    [~,location]=sort(score_matrix(m,:),2,'descend');
    if find(location(1)==m)
        rank1_hit=rank1_hit+1;
    end
    if find(location(1:5)==m)
        rank5_hit=rank5_hit+1;
    end
    if find(location(1:10)==m)
        rank10_hit=rank10_hit+1;
    end
    if find(location(1:20)==m)
        rank20_hit=rank20_hit+1;
    end
end
rank_acc=[rank1_hit/problen,rank5_hit/problen,rank10_hit/problen,rank20_hit/problen];
fin=fopen(param.result_save_file,'a');
fprintf(fin,'2below split_index:%d,iter:%d, rank1:%f,rank5:%f,rank10:%f,rank20:%f\n',split_index,iter,rank_acc(1),rank_acc(2),rank_acc(3),rank_acc(4));
fprintf('split_index:%d,iter:%d, rank1:%f,rank5:%f,rank10:%f,rank20:%f\n',split_index,iter,rank_acc(1),rank_acc(2),rank_acc(3),rank_acc(4));
fclose(fin);


%==============================================================================
% This code is part of the Matlab-based toolbox
% FAIR - Flexible Algorithms for Image Registration.
% For details see
% - https://github.com/C4IR and
% - http://www.siam.org/books/fa06/
%==============================================================================
%
% function [Tc,dT] = nnInterMex(T,omega,x,varargin)
%
% This wrapper runs a CPP file which codes the same scheme as nnInter
% but is hopefully faster, see nnInter for details
%==============================================================================

function [Tc,dT] = nnInterMex(T,omega,x,varargin)

Tc = mfilename('fullpath'); dT = [];
if nargin == 0
  testOneImgModel(mfilename);
    return
elseif nargin == 1 && isempty(T)
    return
end

% flag for computing the derivative
doDerivative = (nargout>1);
matrixFree   = 0;
boundary     = 0; % boundary condition (0: zero padding; 1: replicate)

for k=1:2:length(varargin) % overwrite default parameter
    eval([varargin{k},'=varargin{',int2str(k+1),'};']);
end

dim = length(omega)/2;
m   = size(T);         if dim == 1, m = numel(T); end;
n   = length(x)/dim;

% call CPP subroutine
try
    Tc = nnInterMexC(double(T(:)),omega,m,x(:),boundary==1);
catch err
    FAIRerror(err);
end
if doDerivative
    if matrixFree, dT = zeros(n,dim); else dT = sparse(n,dim*n); end
end
%==============================================================================


function [data,interface,options,idata] = loadData(fname,type,varargin)
% import PoleFigure, EBSD, and ODF data
%
% Description
% *loadData* is a low level method for importing EBSD, PoleFigure, ODF and
% Tensor data from external files. It autodetects the format of the file.
% As parameters the method requires a filename and the crystal and specimen
% @symmetry.
%
% Input
%  fname     - filename
%  cs, ss    - crystal, specimen @symmetry (optional)
%
% Options
%  interface  - specific interface to be used
%  comment    - comment to be associated with the data
%
% Output
%  data - @EBSD, @PoleFigure, @SO3Fun, @tensor, @vector3d, @orientation
%
% See also
% ImportEBSDData EBSD/calcODF ebsd_demo loadEBSD_generic

% process input arguments

% get file names
if nargin < 1
  [fname,PathName] = uigetfile( '*.*',...
    'Select Data files');
  fname = [PathName,fname];
end

% read in directory if needed
if ischar(fname)
  if exist(fname,'dir')
    pname = fname;
  else
    pname = fileparts(fname);
  end
  files = dir(fname);
  files = files(~[files.isdir]);
  assert(~isempty(files),'No file found!');
  if ~isempty(pname) && pname(end)~=filesep, pname = [pname,filesep];end
  fname = strcat(pname,{files.name});
end

% get crystal directions
if ~isempty(varargin) && checkClass(varargin{1},'Miller')
  h = vec2cell(varargin{1});
  varargin = varargin(2:end);
end

% get crystal and specimen symmetry
posCS = find(cellfun(@(x) checkClass(x,'symmetry'),varargin),2);

sym = {};
if ~isempty(posCS)
  cs = varargin{posCS(1)};
  if strcmpi(type,'ODF')
    sym = {'cs',cs};
    clear cs;
  end

end

if numel(posCS) ==2
  ss = varargin{posCS(2)};
  if strcmpi(type,'ODF')
    sym = [sym,'ss',{ss}];
    clear ss;
  end
end

varargin(posCS) = [];

% ---------- determine interface ---------------------------

if ~check_option(varargin,'interface')
  [interface,options] = check_interfaces(fname{1},type,varargin{:});
else
  interface = get_option(varargin,'interface');
  options = delete_option(varargin,'interface',1);
end

if isempty(interface), return; end

% --------------- import data ---------------------------------

% determine superposition - PoleFigure only
c = get_option(options,'superposition');
if isa(c,'double'), c = {c};end

if numel(fname) <= 3
  InfoLevel = 1;
else
  InfoLevel = 0;
  hw = waitbar(0,'Loading data files.');
end

for k = 1:numel(fname)
  if exist('hw','var')
    [~,fn,ext] = fileparts(fname{k});
    waitbar(k/numel(fname),hw,['Loading data file ',[fn ext]]);
  end

  data{k} = feval(['load' type '_',char(interface)],...
    fname{k},options{:},sym{:},'InfoLevel',InfoLevel);
end
if exist('hw','var'), close(hw);end

idata = cellfun('prodofsize',data);

% ------------- apply options ----------------------------------

if length(data) == 1 && iscell(data{1}), data = data{1}; end

% set file name
for i = 1:numel(data)
  data{i} = data{i}.setOption('file_name',ls(fname{min(i,length(fname))}));
end

if strcmpi(type,'EBSD') && check_option(varargin,'3d')
  Z = get_option(varargin,'3d',1:numel(data),'double');
  for k=1:numel(data)
    data{k}.z = repmat(Z(k),length(data{k}),1);
  end
  data = [data{:}];
  data.unitCell = calcUnitCell([data.x(:),data.y(:),data.z(:)],varargin{:});
end

% set crystal and specimen symmetry, specimen direction
if ~any(strcmpi(type,{'tensor','vector3d','ODF'}))
  if iscell(data)
    data = cellfun(@(d,f) setOption(d,'file_name',strtrim(ls(f))),data,fname,'UniformOutput',false);
    data = [data{:}];
  end

  if exist('cs','var'), data.CS = cs;end
  if exist('ss','var') && ~isa(data,'EBSD'), data.SS = ss;end % TODO
  if exist('h','var'),  data = set(data,'h',h);end
  if ~isempty_cell(c),  data = set(data,'c',c);end
elseif ~any(strcmpi(type,{'ODF'}))
  data = [data{:}];

  if exist('cs','var')
    if iscell(data)
      for i = 1:numel(data), data{i}.CS = cs; end
    else
      data.CS = cs;
    end
    if exist('ss','var')
      if iscell(data)
        for i = 1:numel(data), data{i}.SS = ss; end
      else
        data.SS = ss;
      end
    end
  end

end

% rotate data
if check_option(varargin,'rotate')
  data = rotate(data,axis2quat(zvector,get_option(varargin,'rotate')));
end

% --------------------------------------------------------------
function v = checkClass(var,className)

if iscell(var) && ~isempty(var)
  v = any(cellfun('isclass',var,className));
else
  v = isa(var,className);
end


function [obj] = ft_convert_units(obj, target, varargin)

% FT_CONVERT_UNITS changes the geometrical dimension to the specified SI unit.
% The units of the input object is determined from the structure field
% object.unit, or is estimated based on the spatial extend of the structure,
% e.g. a volume conduction model of the head should be approximately 20 cm large.
%
% Use as
%   [object] = ft_convert_units(object, target)
%
% The following geometrical objects are supported as inputs
%   electrode or gradiometer array, see FT_DATATYPE_SENS
%   volume conductor, see FT_DATATYPE_HEADMODEL
%   anatomical mri, see FT_DATATYPE_VOLUME
%   segmented mri, see FT_DATATYPE_SEGMENTATION
%   dipole grid definition, see FT_DATATYPE_SOURCE
%
% Possible target units are 'm', 'dm', 'cm ' or 'mm'. If no target units
% are specified, this function will only determine the native geometrical
% units of the object.
%
% See also FT_DETERMINE_UNITS, FT_CONVERT_COORDSYS, FT_DETERMINE_COODSYS

% Copyright (C) 2005-2016, 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$

% This function consists of three parts:
%   1) determine the input units
%   2) determine the requested scaling factor to obtain the output units
%   3) try to apply the scaling to the known geometrical elements in the input object

% the "target" input argument has been made required in Aug 2017
% prior to that it was also possible to use this function to estimate units
% the backward compatibility support can be removed in Aug 2018
if nargin<2
  ft_warning('calling this function only to determine units is deprecated, please use FT_DETERMINE_UNITS instead');
  obj = ft_determine_units(obj);
  return
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% parse input options
feedback = ft_getopt(varargin, 'feedback', false);

if isstruct(obj) && numel(obj)>1
  % deal with a structure array
  for i=1:numel(obj)
    tmp(i) = ft_convert_units(obj(i), target, varargin{:});
  end
  obj = tmp;
  return
elseif iscell(obj) && numel(obj)>1
  % deal with a cell-array
  % this might represent combined EEG, ECoG and/or MEG
  for i=1:numel(obj)
    obj{i} = ft_convert_units(obj{i}, target, varargin{:});
  end
  return
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% determine the units of the input object
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
obj = ft_determine_units(obj);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% compute the scaling factor from the input units to the desired ones
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if isequal(obj.unit, target)
  % there is nothing to do
  return
end

if istrue(feedback)
  % give some information about the conversion
  fprintf('converting units from ''%s'' to ''%s''\n', obj.unit, target)
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% apply the scaling factor
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
scale = ft_scalingfactor(obj.unit, target);

% volume conductor model
if isfield(obj, 'r'), obj.r = scale * obj.r; end
if isfield(obj, 'o'), obj.o = scale * obj.o; end
if isfield(obj, 'bnd') && isfield(obj.bnd, 'pnt')
  for i=1:length(obj.bnd)
    obj.bnd(i).pnt = scale * obj.bnd(i).pnt;
  end
end
if isfield(obj, 'bnd') && isfield(obj.bnd, 'pos')
  for i=1:length(obj.bnd)
    obj.bnd(i).pos = scale * obj.bnd(i).pos;
  end
end

% old-fashioned gradiometer array
if isfield(obj, 'pnt1'), obj.pnt1 = scale * obj.pnt1; end
if isfield(obj, 'pnt2'), obj.pnt2 = scale * obj.pnt2; end
if isfield(obj, 'prj'),  obj.prj  = scale * obj.prj;  end

% gradiometer array, electrode array, head shape or dipole grid
if isfield(obj, 'pnt'),        obj.pnt        = scale * obj.pnt;        end
if isfield(obj, 'pos'),        obj.pos        = scale * obj.pos;        end
if isfield(obj, 'chanpos'),    obj.chanpos    = scale * obj.chanpos;    end
if isfield(obj, 'chanposorg'), obj.chanposold = scale * obj.chanposorg; end % pre-2016 version
if isfield(obj, 'chanposold'), obj.chanposold = scale * obj.chanposold; end % 2016 version and later
if isfield(obj, 'coilpos'),    obj.coilpos    = scale * obj.coilpos;    end
if isfield(obj, 'elecpos'),    obj.elecpos    = scale * obj.elecpos;    end

% gradiometer array that combines multiple coils in one channel
if isfield(obj, 'tra') && isfield(obj, 'chanunit')
  % find the gradiometer channels that are expressed as unit of field strength divided by unit of distance, e.g. T/cm
  for i=1:length(obj.chanunit)
    tok = tokenize(obj.chanunit{i}, '/');
    if ~isempty(regexp(obj.chanunit{i}, 'm$', 'once'))
      % assume that it is T/m or so
      obj.tra(i,:)    = obj.tra(i,:) / scale;
      obj.chanunit{i} = [tok{1} '/' target];
    elseif ~isempty(regexp(obj.chanunit{i}, '[T|V]$', 'once'))
      % assume that it is T or V, don't do anything
    elseif strcmp(obj.chanunit{i}, 'unknown')
      % assume that it is T or V, don't do anything
    elseif strcmp(obj.chanunit{i}, 'snr')
      %
    else
      ft_error('unexpected units %s', obj.chanunit{i});
    end
  end % for
end % if

% fiducials
if isfield(obj, 'fid') && isfield(obj.fid, 'pnt'), obj.fid.pnt = scale * obj.fid.pnt; end
if isfield(obj, 'fid') && isfield(obj.fid, 'pos'), obj.fid.pos = scale * obj.fid.pos; end

% dipole grid
if isfield(obj, 'resolution'), obj.resolution = scale * obj.resolution; end

% x,y,zgrid can also be 'auto'
if isfield(obj, 'xgrid') && ~ischar(obj.xgrid), obj.xgrid = scale * obj.xgrid; end
if isfield(obj, 'ygrid') && ~ischar(obj.ygrid), obj.ygrid = scale * obj.ygrid; end
if isfield(obj, 'zgrid') && ~ischar(obj.zgrid), obj.zgrid = scale * obj.zgrid; end

% anatomical MRI or functional volume
if isfield(obj, 'transform')
  H = diag([scale scale scale 1]);
  obj.transform = H * obj.transform;
end

if isfield(obj, 'transformorig')
  H = diag([scale scale scale 1]);
  obj.transformorig = H * obj.transformorig;
end

% remove initial and params structure if they exist
if isfield(obj, 'initial') && ~strcmp(target, 'mm')
  obj = rmfield(obj, 'initial');
  ft_warning('Removing field "initial" because potential transformations of normalised volumes only work if geometrical values are expressed in "mm"');
end

if isfield(obj, 'params') && ~strcmp(target, 'mm')
  ft_warning('Removing field "params" because potential transformations of normalised volumes only work if geometrical values are expressed in "mm"');
  obj = rmfield(obj, 'params');
end

% sourcemodel obtained through mne also has a orig-field with the high
% number of vertices
if isfield(obj, 'orig')
  if isfield(obj.orig, 'pnt')
    obj.orig.pnt = scale * obj.orig.pnt;
  end
  if isfield(obj.orig, 'pos')
    obj.orig.pos = scale * obj.orig.pos;
  end
end

% remember the unit
obj.unit = target;


function [taskReport, essentialGenes, taskStructure]=checkTasksGenes(model,inputFile,printOutput,printOnlyFailed,getEssential,taskStructure)
% checkTasksGenes
%   Performs a set of simulations as defined in a task file. This function
%   is identical to "checkTasks", except it allows for the determination of
%   essential genes, rather than essential reactions.
%
%   model           a model structure
%   inputFile       a task list in Excel format. See the function
%                   parseTaskList for details (opt if taskStructure is
%                   supplied)
%   printOutput     true if the results of the test should be displayed
%                   (opt, default true)
%   printOnlyFailed true if only tasks that failed should be displayed
%                   (opt, default false)
%   getEssential    true if the essential genes should be calculated for
%                   all the tasks. (opt, default false)
%   taskStructure   structure with the tasks, as from parseTaskList. If
%                   this is supplied then inputFile is ignored (opt)
%
%   taskReport          structure with the results
%       id              cell array with the id of the task
%       description     cell array with the description of the task
%       ok              boolean array with true if the task was successful
%   essentialGenes      MxN matrix with the essential genes (M) for each
%                       task (N). An element is true if the corresponding
%                       gene is essential in the corresponding task.
%                       Failed tasks and SHOULD FAIL tasks are ignored.
%                       If getEssential is false, then essentialRxns will
%                       be returned as: false(nGenes,nTasks).
%   taskStructure       structure with the tasks, as from parseTaskList
%
%   This function is used for defining a set of tasks for a model to
%   perform. The tasks are defined by defining constraints on the model,
%   and if the problem is feasible, then the task is considered successful.
%   In general, each row can contain one constraint on uptakes, one
%   constraint on outputs, one new equation, and one change of reaction
%   bounds. If more bounds are needed to define the task, then several rows
%   can be used for each task.
%
%   Usage: [taskReport, essentialGenes, taskStructure]=checkTasksGenes(model,inputFile,...
%           printOutput,printOnlyFailed,getEssential,taskStructure)
%

if nargin<3 || isempty(printOutput)
    printOutput=true;
end
if nargin<4 || isempty(printOnlyFailed)
    printOnlyFailed=false;
end
if nargin<5 || isempty(getEssential)
    getEssential=false;
end

%Prepare the input model
model.b=zeros(numel(model.mets),2);

modelMets=upper(strcat(model.metNames,'[',model.comps(model.metComps),']'));
if ~isfield(model,'unconstrained')
    EM='Exchange metabolites should normally not be removed from the model when using checkTasks. Inputs and outputs are defined in the task file instead. Use importModel(file,false) to import a model with exchange metabolites remaining';
    dispEM(EM,false);
end

%Parse the task file
if nargin<6
    taskStructure=parseTaskList(inputFile);
end

essentialGenes=false(numel(model.genes),numel(taskStructure));

tModel=model;
taskReport=[];
for i=1:numel(taskStructure)
    taskReport.id{i,1}=taskStructure(i).id;
    taskReport.description{i,1}=taskStructure(i).description;
    %Set the inputs
    if ~isempty(taskStructure(i).inputs)
        [I, J]=ismember(upper(taskStructure(i).inputs),modelMets);
        J=J(I); %Only keep the ones with matches
        K=ismember(upper(taskStructure(i).inputs),'ALLMETS');
        L=~cellfun('isempty',strfind(upper(taskStructure(i).inputs),'ALLMETSIN'));
        %Check that all metabolites are either real metabolites or
        %ALLMETS/ALLMETSIN
        if ~all(I|K|L)
            fprintf(['ERROR: Could not find all inputs in "[' taskStructure(i).id '] ' taskStructure(i).description '"\n']);
            taskReport.ok(i,1)=false;
            tModel=model;
            continue;
        end
        if numel(J)~=numel(unique(J))
            EM=['The constraints on some input(s) in "[' taskStructure(i).id '] ' taskStructure(i).description '" are defined more than one time'];
            dispEM(EM);
        end
        %If all metabolites should be added
        if any(K)
            %Check if ALLMETS is the first metabolite. Otherwise print a
            %warning since it will write over any other constraints that
            %are set
            if K(1)==0
                EM=['ALLMETS is used as an input in "[' taskStructure(i).id '] ' taskStructure(i).description '" but it it not the first metabolite in the list. Constraints defined for the metabolites before it will be over-written'];
                dispEM(EM,false);
            end
            %Use the first match of ALLMETS. There should only be one, but
            %still..
            tModel.b(:,1)=taskStructure(i).UBin(find(K,1))*-1;
        end
        %If metabolites in a specific compartment should be used
        if any(L)
            L=find(L);
            for j=1:numel(L)
                %The compartment defined
                compartment=upper(taskStructure(i).inputs{L(j)}(11:end-1));
                %Check if it exists in the model
                C=find(ismember(upper(model.comps),compartment));
                if any(C)
                    %Match to metabolites
                    tModel.b(model.metComps==C,1)=taskStructure(i).UBin(L(j))*-1;
                else
                    EM=['The compartment defined for ALLMETSIN in "[' taskStructure(i).id '] ' taskStructure(i).description '" does not exist'];
                    dispEM(EM);
                end
            end
        end
        %Then add the normal constraints
        if any(J)
            tModel.b(J,1)=taskStructure(i).UBin(I)*-1;
            tModel.b(J,2)=taskStructure(i).LBin(I)*-1;
        end
    end
    %Set the outputs
    if ~isempty(taskStructure(i).outputs)
        [I, J]=ismember(upper(taskStructure(i).outputs),modelMets);
        J=J(I); %Only keep the ones with matches
        K=ismember(upper(taskStructure(i).outputs),'ALLMETS');
        L=~cellfun('isempty',strfind(upper(taskStructure(i).outputs),'ALLMETSIN'));
        %Check that all metabolites are either real metabolites or
        %ALLMETS/ALLMETSIN
        if ~all(I|K|L)
            fprintf(['ERROR: Could not find all outputs in "[' taskStructure(i).id '] ' taskStructure(i).description '"\n']);
            taskReport.ok(i,1)=false;
            tModel=model;
            continue;
        end
        if numel(J)~=numel(unique(J))
            EM=['The constraints on some output(s) in "[' taskStructure(i).id '] ' taskStructure(i).description '" are defined more than one time'];
            dispEM(EM);
        end
        %If all metabolites should be added
        if any(K)
            %Check if ALLMETS is the first metabolite. Otherwise print a
            %warning since it will write over any other constraints that
            %are set
            if K(1)==0
                EM=['ALLMETS is used as an output in "[' taskStructure(i).id '] ' taskStructure(i).description '" but it it not the first metabolite in the list. Constraints defined for the metabolites before it will be over-written'];
                dispEM(EM,false);
            end
            %Use the first match of ALLMETS. There should only be one, but
            %still..
            tModel.b(:,2)=taskStructure(i).UBout(find(K,1));
        end
        %If metabolites in a specific compartment should be used
        if any(L)
            L=find(L);
            for j=1:numel(L)
                %The compartment defined
                compartment=upper(taskStructure(i).outputs{L(j)}(11:end-1));
                %Check if it exists in the model
                C=find(ismember(upper(model.comps),compartment));
                if any(C)
                    %Match to metabolites
                    tModel.b(model.metComps==C,2)=taskStructure(i).UBout(L(j));
                else
                    EM=['The compartment defined for ALLMETSIN in "[' taskStructure(i).id '] ' taskStructure(i).description '" does not exist'];
                    dispEM(EM);
                end
            end
        end
        %Then add the normal constraints
        if any(J)
            tModel.b(J,1)=taskStructure(i).LBout(I);
            tModel.b(J,2)=taskStructure(i).UBout(I);
        end
    end
    %Add new rxns
    if ~isempty(taskStructure(i).equations)
        rxn.equations=taskStructure(i).equations;
        rxn.lb=taskStructure(i).LBequ;
        rxn.ub=taskStructure(i).UBequ;
        rxn.rxns=strcat({'TEMPORARY_'},num2str((1:numel(taskStructure(i).equations))'));
        %Allow for new metabolites to be added. This is because it should
        %be possible to add, say, a whole new pathway
        tModel=addRxns(tModel,rxn,3,[],true);
    end
    %Add changed bounds
    if ~isempty(taskStructure(i).changed)
        tModel=setParam(tModel,'lb',taskStructure(i).changed,taskStructure(i).LBrxn);
        tModel=setParam(tModel,'ub',taskStructure(i).changed,taskStructure(i).UBrxn);
    end

    %Solve and print
    sol=solveLP(tModel);
    if ~isempty(sol.x)
        if ~taskStructure(i).shouldFail
            taskReport.ok(i,1)=true;
            if printOnlyFailed==false && printOutput==true
                fprintf(['PASS: [' taskStructure(i).id '] ' taskStructure(i).description '\n']);
            end
            %Calculate the essential reactions
            if getEssential==true
                if ~isfield(tModel,'rules')
                    % the getEssentialGenes function requires the model to
                    % have a "rules" field
                    tModel = generateRules(tModel);
                end
                [~, taskEssential]=getEssentialGenes(tModel);
                essentialGenes(taskEssential,i)=true;
            end
        else
            taskReport.ok(i,1)=false;
            if printOutput==true
                fprintf(['PASS (should fail): [' taskStructure(i).id '] ' taskStructure(i).description '\n']);
            end
        end
    else
        if ~taskStructure(i).shouldFail
            taskReport.ok(i,1)=false;
            if printOutput==true
                fprintf(['FAIL: [' taskStructure(i).id '] ' taskStructure(i).description '\n']);
            end
        else
            taskReport.ok(i,1)=true;
            if printOnlyFailed==false && printOutput==true
                fprintf(['FAIL (should fail): [' taskStructure(i).id '] ' taskStructure(i).description '\n']);
            end
        end
    end
    if taskStructure(i).printFluxes && ~isempty(sol.x)
        sol=solveLP(tModel,1);
        if ~isempty(sol.x)
            printFluxes(tModel,sol.x,false,10^-6,[],'%rxnID (%eqn):%flux\n');
            fprintf('\n');
        end
    end
    tModel=model;
end
end


function [FV] = mesh_bet2matlab(fileprefix)

% mesh_bet2matlab - Read FSL BET tesselation (.coo/.dat)
%
% USEAGE: [FV] = mesh_bet2matlab(fileprefix)
%
% This function will load ascii files created by the FSL
% BET function, see http://www.fmrib.ox.ac.uk/fsl
%
% When used with the -x option, the FSL BET function can
% output the tesselation of the brain surface (ie, only
% a fairly smooth, non-detailed, surface), eg:
%
% bet T1 T1_brain -x
%
% The brain tesselation is output into two files, eg:
%
%   T1_brain.coo
%   T1_brain.dat
%
% The T1_brain.coo file contains the co-ordinates of the
% vertices, and the .dat file contains the details of
% the links between vertices (each line corresponds to
% each vertex in the .coo file, and the 1's correspond
% to which other vertices they are connected to).
%
% The returned FV struct contains the 'vertices' and
% 'faces' matrices, which can be input to the patch
% command, eg:
%
% Hpatch = patch('Vertices',FV.vertices,'Faces',FV.faces,...
%                'EdgeColor',[.8 .8 .8],...
%                'FaceColor',[0.9 0.9 0.9]);
%
% This will plot the mesh as a patch object.  See the patch
% command and matlab help for more information on coloring
% this object.
%
% See also: mesh_freesurfer2matlab
%

% $Revision: 1.1 $ $Date: 2009-04-28 22:13:57 $

% Licence:  GNU GPL, no implied or express warranties
% History:  03/02 Darren.Weber_at_radiology.ucsf.edu
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if ~exist('fileprefix','var'),
    error('No input fileprefix');
end

if findstr('.coo',fileprefix),
    fprintf('MESH_BET2MATLAB: Removing .coo extension from ''%s''\n',fileprefix);
    fileprefix = strrep(fileprefix,'.coo','');
end
if findstr('.dat',fileprefix),
    fprintf('MESH_BET2MATLAB: Removing .dat extension from ''%s''\n',fileprefix);
    fileprefix = strrep(fileprefix,'.dat','');
end

FV.vertices = readCOO(fileprefix);

Nvertices   = size(FV.vertices,1);

edgematrix  = readDAT(fileprefix,Nvertices);

FV.faces    = edge2face(FV.vertices,edgematrix);

return

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function faces = edge2face(vertices,edgematrix)

    Nvertices = size(vertices,1);

    plot3(vertices(:,1),vertices(:,2),vertices(:,3),'b.'), hold on

    for i = 1:Nvertices,

        % Get current vertex XYZ coordinates
        v  = vertices(i,:);

        plot3(v(:,1),v(:,2),v(:,3),'go');

        % Get neighbouring vertex XYZ coordinates
        vn = vertices(find(edgematrix(i,:)),:);

        plot3(vn(:,1),vn(:,2),vn(:,3),'ro');

        faces = tri([v;vn]);

        return

    end

    faces = [];

return

function faces = tri(V)

    % Must identify angle of rotation from
    % central vertex, V(1,:), to all other vertices
    % in a counterclockwise direction

    % First translate all vertices to an origin
    % given by the central vertex
    Vo = V - repmat(V(1,:),size(V,1),1);

    % Find direction cosines for line from centre to vertex
    d = zeros(size(Vo,1),1);
    l = zeros(size(Vo,1),1);
    m = zeros(size(Vo,1),1);
    n = zeros(size(Vo,1),1);

    for i = 1:length(Vo),

        x = Vo(i,1);  y = Vo(i,2);  z = Vo(i,3);

        d(i) = sqrt( (x)^2 + (y)^2 + (z)^2 );

        if d(i) > 0,
            l(i) = x/d(i); % cos alpha
            m(i) = y/d(i); % cos beta
            n(i) = z/d(i); % cos gamma
        end
    end

    faces = [];

    %hold off
    %plot3(Vo(:,1),Vo(:,2),Vo(:,3),'b.'), view(2), hold on

return

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function vertices = readCOO(fileprefix)

	[path,name,ext] = fileparts(strcat(fileprefix,'.coo'));
	file = fullfile(path,[name ext]);

	fid = fopen(file,'r');

	if isequal(fid,-1),
        S=sprintf('Could not open file: "%s"',file);
        error(S);
	else
        fprintf('...Reading BET vertices...');
        tic;

        % Read vertices
        vertices = fscanf(fid,'%s%f%f%f',[4,inf]);
        fclose(fid);

        % remove last row (all zeros) and translate
        vertices = vertices(2:4,:)';

        t = toc;
        fprintf('...done (%6.2f sec).\n',t);
	end

return

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function edgematrix = readDAT(fileprefix,Nvertices)

	[path,name,ext] = fileparts(strcat(fileprefix,'.dat'));
	file = fullfile(path,[name ext]);

	fid = fopen(file,'r');

	if isequal(fid,-1),
        S=sprintf('Could not open file: "%s"',file);
        error(S);
	else
        fprintf('...Reading BET edge matrix...');
        tic;

        % Read faces
        edgematrix = fscanf(fid,'%1d',[Nvertices,Nvertices]);
        fclose(fid);

        t = toc;
        fprintf('...done (%6.2f sec).\n',t);
	end
return


function [lin_restr,nonlin_restr,markov_chains]=create_restrictions_and_markov_chains2(markov_chains)
% create_restrictions_and_markov_chains2 -- creates restrictions and
% markov chains for the SVAR model in which Coefficients and variances have
% different chains, different regimes, and different durations
%
% ::
%
%
%   [lin_restr,nonlin_restr,tpl]=create_restrictions_and_markov_chains2(tpl)
%
% Args:
%
%    - **markov_chains** [empty|struct]: structure of previously defined
%    markov chains
%
% Returns:
%    :
%
%    - **lin_restr** [cell]: cell array of restrictions (see below).
%
%    - **nonlin_restr** [cell]: cell array of inequality restrictions
%
%    - **markov_chains** [struct]: modified markov chains
%
% Note:
%
%    - The syntax to construct a restriction
%      --> ai(eqtn)
%      --> ai(eqtn,vbl)
%      --> ai(eqtn,vbl,chain_name,state)
%      --> a(eqtn)
%      --> a(eqtn,vbl)
%      --> a(eqtn,vbl,chain_name,state)
%      - **eqtn** [integer]: integer
%      - **vbl** [integer|char]: integer or variable name
%      - **i** [integer]: lag
%      - **chain_name** [char]: name of the markov chain
%      - **state** [integer]: state number
%
%    - The lag coefficients are labelled a0, a1, a2,...,ak, for a model with k
%    lags. Obviously, a0 denotes the contemporaneous coefficients.
%
%    - The constant terms labelled c_1_1, c_2_2,...,c_n_n, for a model with n
%    endogenous variables.
%
%    - The standard deviations labelled s_1_1, s_2_2,...,s_n_n, for a
%    model with n endogenous variables.
%
% Example:
%
%    See also:

if nargin==0||isempty(markov_chains)

    markov_chains=struct('name',{},...
    'states_expected_duration',{},...
    'controlled_parameters',{});

end

% We borrow both the restrictions and the markov chains from the model in
% which all coefficients across all equations switch in lockstep.
%--------------------------------------------------------------------------
[lin_restr,nonlin_restr,markov_chains]=create_restrictions_and_markov_chains1(markov_chains);

% Then we add another chain controling all variances across all equations
%-------------------------------------------------------------------------
% We just make sure we do not change the restrictions
last=numel(markov_chains);

markov_chains(last+1)=struct('name','syncvol',...
    'states_expected_duration',[2+1i,2+1i,2+1i],...
    'controlled_parameters',{{'s'}});

end


function X = Yi2X(Y, i)
if i == 3
    X = shiftdim(Trans_Faces(Y),i+1);
elseif i == 2
    X = shiftdim(Trans_Faces(Y),i);
else
    X = shiftdim(Trans_Faces(Y),i-1);
end
end


function [ cost, grad, numTotal, pred_cell ] = drdae_discrim_joint_kl_obj( ...
    theta, eI, data_cell, targets_cell, mixture_spectrum, fprop_only, pred_out)
% Copyright (c) 2014-present University of Illinois at Urbana-Champaign
% All rights reserved.
%
% Developed by:     Po-Sen Huang, Paris Smaragdis
%                   Department of Electrical and Computer Engineering
%                   Department of Computer Science
%
% discrim. training + joint masking
%
%PRNN_OBJ MinFunc style objective for Deep Recurrent Denoising Autoencoder
%   theta is the full parameter vector
%   eI contains experiment / network architecture
%   data_cell is a cell array of matrices. Each a distinct length is a cell
%             entry. Each matrix has a time series example in each column
%   targets_cell is parallel to data, but contains the labels for each time
%   fprop_only is a flag that only computes the cost, no gradient
%   numTotal is total number of frames evaluated
%   pred_out is a binary flag for whether pred_cell is populated
%            pred_cell only filled properly when utterances one per cell

%% Debug: Turns this into an identity-function for debugging rest of system
if isfield(eI, 'objReturnsIdentity') && eI.objReturnsIdentity
    cost = 0; grad = 0; numTotal = 0;
    for l = 1:numel(data_cell)
        numUtterances = size(data_cell{l}, 2);
        original_vector = reshape(data_cell{l}, eI.winSize*eI.featDim, []);
        midPnt = ceil(eI.winSize/2);
        original_vector = original_vector((midPnt-1)*14+1 : midPnt*14, :);
        pred_cell{l} = reshape(original_vector, [], numUtterances);
    end
    return;
end

%if isempty(return_activation),
  return_activation = 0;
%end

%% Load data from globals if not passed in (happens when run on RPC slave)
global g_data_cell;
global g_targets_cell;
isSlave = false;
if isempty(data_cell)
    data_cell = g_data_cell;
    targets_cell = g_targets_cell;
    isSlave = true;
end;
pred_cell = cell(1,numel(data_cell));
act_cell = cell(1,numel(data_cell));
%% default short circuits to false
if ~isfield(eI, 'shortCircuit')
    eI.shortCircuit = 0;
end;

%% default dropout to false
if ~isfield(eI, 'dropout')
  eI.dropout = 0;
end;

%% setup weights and accumulators
[stack, W_t] = rnn_params2stack(theta, eI);
cost = 0; numTotal = 0;
outputDim = eI.layerSizes(end);

%% setup structures to aggregate gradients
stackGrad = cell(1,numel(eI.layerSizes));

if isfield(eI, 'fullRNN') && eI.fullRNN==1
   W_t_grad = cell(1,numel(eI.layerSizes)-1);
    for l = 1:numel(eI.layerSizes)-1
        W_t_grad{l}.W = zeros(size(W_t{l}.W));
    end
else
   W_t_grad = zeros(size(W_t));
end

for l = 1:numel(eI.layerSizes)
    stackGrad{l}.W = zeros(size(stack{l}.W));
    stackGrad{l}.b = zeros(size(stack{l}.b));
end
if eI.shortCircuit
    stackGrad{end}.W_ss = zeros(size(stack{end}.W_ss));
end;
%% check options
if ~exist('fprop_only','var')
    fprop_only = false;
end;
if ~exist('pred_out','var')
    pred_out = false;
end;

% DROPOUT: vector of length of hidden layers with 0 or 1
% (to drop or keep activation unit) with prob=0.5
hActToDrop = cell(numel(eI.layerSizes),1);
for i=1:numel(eI.layerSizes)-1
 if eI.dropout
   hActToDrop{i} = 1/eI.dropout * binornd(1,eI.dropout, eI.layerSizes(i),1);
   %hActToDrop{i} = round(rand(eI.layerSizes(i),1));
 else
   hActToDrop{i} = ones(eI.layerSizes(i),1);
 end
end

%% loop over each distinct length
for c = 1:numel(data_cell)
    if isempty(data_cell{c}), continue; end

    data = data_cell{c};
    targets = {};
    if ~isempty(targets_cell), targets = targets_cell{c}; end;
    uttPred = [];
    T =size(data,1) / eI.inputDim;
    % store hidden unit activations at each time instant
    hAct = cell(numel(eI.layerSizes)-1, T);
    for t = 1:T
        %% forward prop all hidden layers
        for l = 1:numel(eI.layerSizes)-1
            if l == 1
                hAct{1,t} = stack{1}.W * data((t-1)*eI.inputDim+1:t*eI.inputDim, :);
            else
                hAct{l,t} = stack{l}.W * hAct{l-1,t};
            end;
            hAct{l,t} = bsxfun(@plus, hAct{l,t}, stack{l}.b);
            % temporal recurrence. limited to single layer for now
            if t > 1
                if isfield(eI, 'fullRNN') && eI.fullRNN==1
                    hAct{l,t} = hAct{l,t} + W_t{l}.W * hAct{l,t-1};
                elseif l == eI.temporalLayer
                    hAct{l,t} = hAct{l,t} + W_t * hAct{l,t-1};
                end
            end;

            % nonlinearity
            if strcmpi(eI.activationFn,'tanh')
                hAct{l,t} = tanh(hAct{l,t});
            elseif strcmpi(eI.activationFn,'logistic')
                hAct{l,t} = 1./(1+exp(-hAct{l,t}));
            elseif strcmpi(eI.activationFn,'RELU')
                hAct{l,t} = max(0,hAct{l,t});
            else
                error('unrecognized activation function: %s',eI.activationFn);
            end;
            %dropout (hActToDrop will be all ones if no dropout specified)
            hAct{1,t} = bsxfun(@times, hAct{1,t}, hActToDrop{l});
        end;
        % forward prop top layer not done here to avoid caching it
    end;
    %% compute cost and backprop through time
    if  eI.temporalLayer
        if isfield(eI, 'fullRNN') && eI.fullRNN==1
            delta_t = cell(1, numel(eI.layerSizes)-1);
            for l = 1:numel(eI.layerSizes)-1
                delta_t{l} = zeros(eI.layerSizes(l),size(data,2));
            end
        else
            delta_t = zeros(eI.layerSizes(eI.temporalLayer),size(data,2));
        end
    end;

    y1_dim= 1:outputDim/2;
    y2_dim= outputDim/2+1:outputDim;
    mixtures=mixture_spectrum{c};

    for t = T:-1:1
        l = numel(eI.layerSizes);
        %% forward prop output layer for this timestep
        curPred = bsxfun(@plus, stack{l}.W * hAct{l-1,t}, stack{l}.b);

        if eI.outputnonlinear==1,
           if strcmpi(eI.activationFn,'tanh')
                curPred = tanh(curPred);
           elseif strcmpi(eI.activationFn,'logistic')
                curPred = 1./(1+exp(-curPred));
           elseif strcmpi(eI.activationFn,'RELU')
                curPred = max(0,curPred);
           else
                error('unrecognized activation function: %s',eI.activationFn);
           end
        end

        mixture=mixtures((t-1)*numel(y1_dim)+1:t*numel(y1_dim),:);
        a1 = curPred(y1_dim,:); a2 = curPred(y2_dim,:);

        const=eI.const;%1e-8 ;
        const2=eI.const2;% 1e-3;

        if strcmp(eI.opt,'softlinear'),
            y1= (a1)./((a1)+(a2)+1e-10).* mixture;
            y2= (a2)./((a1)+(a2)+1e-10).* mixture;
        elseif strcmp(eI.opt,'softabs'),
            y1= abs(a1)./(abs(a1)+abs(a2)+1e-10).* mixture;
            y2= abs(a2)./(abs(a1)+abs(a2)+1e-10).* mixture;
        elseif strcmp(eI.opt,'softabs_const') || strcmp(eI.opt,'softabs_kl_const'),
            y1= abs(a1)./(abs(a1)+abs(a2)+const).* mixture;
            y2= abs(a2)./(abs(a1)+abs(a2)+const).* mixture;
        elseif strcmp(eI.opt, 'softquad')
            y1= (a1.^2)./((a1.^2)+(a2.^2)+1e-10).* mixture;
            y2= (a2.^2)./((a1.^2)+(a2.^2)+1e-10).* mixture;
        else
        end
        weighted_curPred=[y1; y2];

        % add short circuit to regression prediction if model has it
        if eI.shortCircuit
            weighted_curPred = weighted_curPred + stack{end}.W_ss ...
                * data((t-1)*eI.inputDim+1:t*eI.inputDim, :);
        end;
        if pred_out, uttPred = [weighted_curPred, uttPred]; end;
        % skip loss computation if no targets given
        if isempty(targets), continue; end;

        curTargets = targets((t-1)*outputDim+1:t*outputDim, :);
        curTargets_neg = [curTargets(outputDim/2+1:outputDim,:); curTargets(1:outputDim/2,:)];

        y_t = (1- eI.r) * weighted_curPred + eI.r * curTargets_neg - curTargets;

        ya_ta= y_t(y1_dim,:);
        yb_tb= y_t(y2_dim,:);

        if strcmp(eI.opt,'softlinear'),
            delta_y1 =  (ya_ta-yb_tb).* y2./(a1+a2+1e-10);
            delta_y2 = (-ya_ta+yb_tb) .* y1./ (a1+a2+1e-10);
        elseif strcmp(eI.opt,'softabs'),
            delta_y1 =  (ya_ta-yb_tb).* y2./(abs(a1)+abs(a2)+1e-10);
            delta_y2 = (-ya_ta+yb_tb) .* y1./ (abs(a1)+abs(a2)+1e-10);

            delta_y1(a1<0) = -delta_y1(a1<0);
            delta_y2(a2<0) = -delta_y2(a2<0);
        elseif strcmp(eI.opt,'softabs_const'),
             const_div=const./((abs(a1)+abs(a2)+const).^2).* mixture;
             delta_y1 =  (ya_ta-yb_tb).* y2./(abs(a1)+abs(a2)+const);
             delta_y1 = delta_y1+ ya_ta.* const_div;
             delta_y2 = (-ya_ta+yb_tb) .* y1./ (abs(a1)+abs(a2)+const);
             delta_y2= delta_y2+ yb_tb.* const_div;

             delta_y1(a1<0) =  -delta_y1(a1<0);
             delta_y2(a2<0) =  -delta_y2(a2<0);
        elseif strcmp(eI.opt,'softabs_kl_const'),
             y_target_a = curTargets(y1_dim, :);
             y_target_b = curTargets(y2_dim, :);
             y_target_neg_a = y_target_b;
             y_target_neg_b = y_target_a;

             y_pred_a = weighted_curPred(y1_dim, :);
             y_pred_b = weighted_curPred(y2_dim, :);

             const_div=const./((abs(a1)+abs(a2)+const).^2).* mixture;

             delta_y1 =  (-y_target_a./(y_pred_a+const2) + y_target_b./(y_pred_b+const2)).* y2./(abs(a1)+abs(a2)+const);
             delta_y1 = delta_y1+  (-y_target_a./(y_pred_a+const2)+1).* const_div;

             delta_y2 =  (y_target_a./(y_pred_a+const2) - y_target_b./(y_pred_b+const2)).* y1./(abs(a1)+abs(a2)+const);
             delta_y2= delta_y2+ (-y_target_b./(y_pred_b+const2)+1) .* const_div;

             % discrim part
             delta_y1 =  delta_y1- eI.r* (-y_target_neg_a./(y_pred_a+const2) + y_target_neg_b./(y_pred_b+const2)).* y2./(abs(a1)+abs(a2)+const);
             delta_y1 = delta_y1-   eI.r* (-y_target_neg_a./(y_pred_a+const2)+1).* const_div;

             delta_y2 = delta_y2- eI.r*(y_target_neg_a./(y_pred_a+const2) - y_target_neg_b./(y_pred_b+const2)).* y1./(abs(a1)+abs(a2)+const);
             delta_y2= delta_y2-  eI.r*(-y_target_neg_b./(y_pred_b+const2)+1) .* const_div;

             delta_y1(a1<0) =  -delta_y1(a1<0);
             delta_y2(a2<0) =  -delta_y2(a2<0);
        elseif strcmp(eI.opt, 'softquad')
            delta_y1 =  (ya_ta-yb_tb).* (2*a1.*y2)./ (a1.^2+a2.^2+1e-10); %  y1= (a1.^2)./((a1.^2)+(a2.^2)+1e-8);%.* mixture;
            delta_y2 =  (-ya_ta+yb_tb).* (2*a2.*y1)./ (a1.^2+a2.^2+1e-10);%         y2= (a2.^2)./((a1.^2)+(a2.^2)+1e-8);%.* mixture;
        else
        end

        delta = [ delta_y1; delta_y2 ];
        if strcmp(eI.opt,'softlinear') || strcmp(eI.opt,'softabs') || strcmp(eI.opt, 'softquad') || strcmp(eI.opt,'softabs_const'),
             cost = cost + 0.5 * ( sum( sum((weighted_curPred - curTargets).^2)) ...
                -  eI.r* sum( sum((weighted_curPred - curTargets_neg).^2)));
        elseif strcmp(eI.opt,'softabs_kl_const'),
             cost = cost +...
             sum(sum( curTargets.*log( curTargets./(weighted_curPred + const2) + const2 )-curTargets+ weighted_curPred +const2))...
            -eI.r* sum(sum( curTargets_neg.*log( curTargets_neg./(weighted_curPred + const2) + const2 )-curTargets_neg+ weighted_curPred +const2));
        else

        end

        if eI.outputnonlinear==1,
             if strcmpi(eI.activationFn,'tanh')
                delta = delta .* (1 -curPred.^2);
            elseif strcmpi(eI.activationFn,'logistic')
                delta = delta .* curPred .* (1 - curPred);
            elseif strcmpi(eI.activationFn,'RELU')
                delta = delta .* double(curPred>0);
            else
                error('unrecognized activation function: %s',eI.activationFn);
            end;
        end

        if fprop_only, continue; end;
        %% regression layer gradient and delta
        stackGrad{l}.W = stackGrad{l}.W + delta * hAct{l-1,t}';
        stackGrad{l}.b = stackGrad{l}.b + sum(delta,2);
        % short circuit layer

        if eI.shortCircuit
            stackGrad{end}.W_ss = stackGrad{end}.W_ss + delta ...
                * data((t-1)*eI.inputDim+1:t*eI.inputDim, :)';
        end;
        delta = stack{l}.W' * delta;
        %% backprop through hidden layers
        for l = numel(eI.layerSizes)-1:-1:1
            % aggregate temporal delta term if this is the recurrent layer
            if isfield(eI, 'fullRNN') && eI.fullRNN==1
                delta = delta + delta_t{l};
            elseif l == eI.temporalLayer
              delta = delta + delta_t;
            else
            end
            % push delta through activation function for this layer
            % tanh unit choice assumed
            if strcmpi(eI.activationFn,'tanh')
                delta = delta .* (1 - hAct{l,t}.^2);
            elseif strcmpi(eI.activationFn,'logistic')
                delta = delta .* hAct{l,t} .* (1 - hAct{l,t});
            elseif strcmpi(eI.activationFn,'RELU')
                delta = delta .* double(hAct{l,t}>0);
            else
                error('unrecognized activation function: %s',eI.activationFn);
            end;
            % gradient of bottom-up connection for this layer
            if l > 1
                stackGrad{l}.W = stackGrad{l}.W + delta * hAct{l-1,t}';
            else
                stackGrad{l}.W = stackGrad{l}.W + delta * data((t-1)*eI.inputDim+1:t*eI.inputDim, :)';
            end;
            % gradient for bias
            stackGrad{l}.b = stackGrad{l}.b + sum(delta,2);

            % compute derivative and delta for temporal connections
            if t > 1
                if isfield(eI, 'fullRNN') && eI.fullRNN==1
                     W_t_grad{l}.W = W_t_grad{l}.W + delta * hAct{l,t-1}';
                     % push delta through temporal weights
                     delta_t{l} = W_t{l}.W' * delta;
                elseif l == eI.temporalLayer
                     W_t_grad = W_t_grad + delta * hAct{l,t-1}';
                     % push delta through temporal weights
                     delta_t = W_t' * delta;
                end
            end
            % push delta through bottom-up weights
            if l > 1
                delta = stack{l}.W' * delta;
            end;
        end
        % reduces avg memory usage but doesn't reduce peak
        %hAct(:,t) = [];
    end
    pred_cell{c} = uttPred;
    % Return the activations for this utterance.
    if return_activation,
      act_cell{c} = cell2mat(hAct);
    end
    % keep track of how many examples seen in total
    numTotal = numTotal + T * size(targets,2);
end

%% stack gradients into single vector and compute weight cost
wCost = numTotal * eI.lambda * sum(theta.^2);
grad = rnn_stack2params(stackGrad, eI, W_t_grad, true);
grad = grad + 2 * numTotal * eI.lambda * theta;

%% clipping
if isfield(eI,'clip') && eI.clip~=0, % if eI.clip==0, no clip
  if eI.clip > 0 % method one -clip the whole
      norm_grad = norm(grad);
      fprintf('norm_grad:%f\n', norm_grad);
      % avoid numerial problem
      if norm_grad <0 || norm_grad > 1e15 || isnan(norm_grad) || isinf(norm_grad),
          grad = zeros(size(grad));
          fprintf('set gradient to zeros\n');
      end
      if norm_grad > eI.clip
         grad = eI.clip * grad/ norm_grad;
      end
  else % method two - clip each entry
      clip_value = -1*eI.clip;
      grad(grad > clip_value)=clip_value;
      grad(grad < -clip_value)=-clip_value;
  end
end

%%
avCost = cost/numTotal;
avWCost = wCost/numTotal;
cost = cost + wCost;

% print output
if ~isSlave && ~isempty(targets_cell)
    fprintf('loss:  %f  wCost:  %f \t',avCost, avWCost);

    if isfield(eI, 'fullRNN') && eI.fullRNN==1
        fprintf('wNorm: %f  rNorm: %f  oNorm: %f\n',sum(stack{1}.W(:).^2),...
            sum(W_t{1}.W(:).^2), sum(stack{end}.W(:).^2));
    else
        fprintf('wNorm: %f  rNorm: %f  oNorm: %f\n',sum(stack{1}.W(:).^2),...
            sum(W_t(:).^2), sum(stack{end}.W(:).^2));
    end
end;