function [mesh, meshCurvature, layerCurvature, gLocs2d, gLocs3d, numNodes] ...
    = mfmAssignGray(mesh, params, insideNodes)
% Assign gray matter locations to the unfolded positions in a flattened
% map.
%
% [mesh, meshCurvature, layerCurvature, gLocs2d, gLocs3d, numNodes] ...
%    = mfmAssignGray(mesh, params, insideNodes)
%
% Author:  Winawer
%
%   Purpose: Assign gray matter locations to the unfolded positions in a flattened
%   map.
%
%   Sub-routine derived from Alex's unfoldMeshFromGUI code.
%
% See Also:  unfoldMeshFromGUI
% Get all the variables we may need
meshFileName     = params.meshFileName;
grayFileName     = params.grayFileName;
flatFileName     = params.flatFileName;
startCoords      = params.startCoords;
scaleFactor      = params.scaleFactor;
perimDist        = params.perimDist;
statusHandle     = params.statusHandle;
busyHandle       = params.busyHandle;
spacingMethod    = params.spacingMethod;
adjustSpacing    = params.adjustSpacing;
gridSpacing      = params.gridSpacing;
showFigures      = params.showFigures;
saveExtra        = params.saveExtra;
truePerimDist    = params.truePerimDist;
hemi             = params.hemi;
nperims          = params.NPERIMS;
saveIntermeidate = params.SAVE_INTERMEDIATE;
numberOfSteps    = params.NUMBEROFSTEPS;

% Update status
statusStringAdd(statusHandle,'Reading gray graph...');
[gNodes, gEdges] = readGrayGraph(grayFileName, hemi);

% Do all the gray node stages in a loop so that we can have arbitrary
% numbers of layers.
numGrayLayers=max(gNodes(6,:));

% Get the indices for all the gnodes (all layers)
for t=1:numGrayLayers
    grayNodeIndices{t}=find(gNodes(6,:)==t);
end

% Extract the layer 1 nodes. These are special because they are used to map
% down to the unfolded boundary mesh.
l1gNodes=gNodes(:,grayNodeIndices{1});
l1mesh.vertices=l1gNodes(1:3,:);
l1mesh.indices=grayNodeIndices{1};

% How many gNodes are there?
nGnodes=length(gNodes);
% How many gEdges are there?
nGedges=length(gEdges);

% We want to make a grey node connection matrix - which grey nodes are connected to which other gnodes?
statusStringAdd(statusHandle,'Finding grey connection matrix (slow)');

grayConMat=makeGrayConMat(gNodes,gEdges,busyHandle);

% We can assign layer 1 grey nodes to the white matter mesh using
% assignToNearest.dll (see assignToNearest.c) Can't do this for higher
% levels of grey matter because they might be mis-assigned. (Also, potential
% problem near  very crinkly edges. - Could we accidentally assign a l1
% grey matter node to the wrong WM point?) (I think the answer is 'yes,
% rarely'. If a single layer or gray is sandwiched between two sides of a
% sulcus (say) : It's grown from one side but mrFlatMesh has no way of
% telling which one.  Going deeper into mrGray's source code to determine
% the parentage of l1 gray nodes might be possible...)

% So for higher grey matter points, we have to restrict the possible sub
% node search space by assigning them >only< to  points they are connected
% to. Note that a single layer2 grey node may be connected to several l1
% nodes

% The gray may be defined over the entire mesh but we only want to deal with gray points over the
% unfolded part. The strategy should be....
% 1) do assignToNearest for each mesh point to find the nearest connected l1 node
% 2) Use the set of l1 nodes found in 1) to build up a list of other connected gray nodes
% 3) Repeat stage 2 for l3,l4

statusStringAdd(statusHandle,'Mapping L1 to mesh.');

% Find 3D coords of all the l1 gnodes
l1GNodeCoords=l1gNodes(1:3,:)';

% Find 3D coords of all the mesh points (not just the unfolded ones)
meshCoords=mesh.uniqueVertices;

% Mesh coords are in voxels, gnode coords are in voxels. Bring them into alignment
l1GNodeCoordsN=l1GNodeCoords-1; % Use this -1 to bring them into perfect alignment. This was the source of an error in V1.0

% Now restrict the mesh coords to just those points in the unfold
meshCoordsN=meshCoords(mesh.insideNodes,:);

% And now restrict the set of l1 gray nodes so that only those that are relatively near the
% mesh are included in the search - this is done first as a simple bounds check
boundedL1NodeIndices=boundsCheck3D(min(meshCoordsN)-3,max(meshCoordsN)+3,l1GNodeCoordsN);
boundedL1GNodes=l1GNodeCoordsN(boundedL1NodeIndices,:);
boundedL1NodeIndices=grayNodeIndices{1}(boundedL1NodeIndices); % This is now a list of indices into the full gNode array

statusStringAdd(statusHandle,'Finding nearest Ll gray points to mesh (very slow)');

% Find mesh points near the l1 gNodes
[boundedL1toMeshIndices,sqrDist]=nearpoints(boundedL1GNodes',(meshCoordsN)');
% This returns a list of indices into the meshCoordsN array that links a single 3D mesh point to each l1Gnode)
% and then eliminate any l1gNodes that are more than a set distance away from the mesh - here sqrt(0.25)

% *************************
closeEnough=find(sqrDist<=0.2501);
% *************************

boundedL1toMeshNodes  = boundedL1GNodes(closeEnough,:); % remember, assignToNearest returns the squared distance
boundedL1toMeshIndices= boundedL1toMeshIndices(closeEnough);

% For each member of the bounded l1Gnodes, this tells us the index of the full mesh point that it maps to.
fullBoundedL1toMeshIndices=insideNodes(boundedL1toMeshIndices);

restNodeIndices{1}=boundedL1NodeIndices(closeEnough);

statusStringAdd(statusHandle,'Setting L1 glocs');
% We can start setting gLocs
% Note the +1 here! Because we subtracted 1 from the original node coords
% to align them with the mesh.
layerGlocs3d{1}=boundedL1toMeshNodes+1;
layerGlocs2d{1}=mesh.X(fullBoundedL1toMeshIndices,:);

numNodes(1)=length(layerGlocs3d{1});

layerCurvature{1}=mesh.uniqueCols(fullBoundedL1toMeshIndices,1);

if (showFigures)
    h = unfoldPlotL1Mesh(grayConMat(restNodeIndices{1},restNodeIndices{1}), layerGlocs2d{1});
    statusStringAdd(statusHandle,'Displaying L1 gray mesh.');

	[p f ext] = fileparts(flatFileName);
	savePath = fullfile(p, [f ' Gray Mesh.png']);
	saveas(h, savePath);
	fprintf('[%s]: Saved %s.\n', mfilename, savePath);
end

% Now we have to find l2tol1Indices, l3tol2Indices and l4tol3Indices. This
% is faster since for each point, we restrict its  potential nearest
% neighbours to points that it is connected to in the previous layer.  We
% also restrict the l2 nodes to just those that are connected to the
% restricted l1 nodes and the l3 nodes to those connected to the l2 nodes.
% Use the full connection matrix to find which l2 Gnodes are connected to the restricted l1Gnodes
statusStringAdd(statusHandle,'Mapping other gray layers (2,3,4..)');

% Set up the 3 critical arrays curvature, gLocs2d, gLocs3d. These are what
% eventually get written to the flat.
meshCurvature=layerCurvature{1};
gLocs2d=layerGlocs2d{1};
gLocs3d=layerGlocs3d{1};

for t=2:numGrayLayers

    layerInterconnect=grayConMat(:, restNodeIndices{t-1}); % Finds all the things connected to the t-1th layer...
    [restNodeIndices{t},dummy]=find(layerInterconnect);
    restNodeIndices{t}=unique(restNodeIndices{t});
    restNodeIndices{t}=intersect(restNodeIndices{t},grayNodeIndices{t}); % Only take potential l2 node indices
    finalConnectIndices{t-1}=findNearestConnected(gNodes', restNodeIndices{t},restNodeIndices{t-1},grayConMat);

    % Set glocs3d, glocs2d, meshCurvature
    layerGlocs3d{t}=gNodes(1:3,restNodeIndices{t})';
    numNodes(t)=length(layerGlocs3d{t});
    layerGlocs2d{t}=layerGlocs2d{t-1}(finalConnectIndices{t-1},:);
    layerCurvature{t}=zeros(length(finalConnectIndices{t-1}),1);
    gLocs2d=[gLocs2d;layerGlocs2d{t}];
    gLocs3d=[gLocs3d;layerGlocs3d{t}];
    meshCurvature=[meshCurvature;layerCurvature{t}];

end

if (showFigures),
	unfoldPlotgLocs2d(numGrayLayers,layerGlocs2d);
	[p f ext] = fileparts(flatFileName);
	savePath = fullfile(p, [f ' Grid Locations.png']);
	saveas(gcf, savePath);
	fprintf('[%s]: Saved %s.\n', mfilename, savePath);
end

statusStringAdd(statusHandle,'Creating flat.mat structure');

% We want to transform  the curvature to run between 1 to 64 for display
% purposes.  We want the mean, however, to be preserved so that we can
% calculate the average curvature and also we want to keep the values
% accurate in terms of real curvature (which is 1/radius in pixels but
% should be mm; though mrGray may do some unwanted scaling).
% So, instead of this:
%     meshCurvature=normalize((meshCurvature))*63+1;%
% we now do this to (a) map to 1 to 64,
%
% Note that the curvature values here are pulled from the rgba field of the
% mesh sturct. This is created by meGray and is the actual RGB (& alpha)
% value used to color each triangle when the mesh is rendered. If the mesh
% has no color overlays, then this will be an 8-bit grayscale value (R=G=B)
% and thus range from 0-255, with curvature=0 at 127. (RFD/BW).
meshCurvature = (layerCurvature{1}/255)*63 ;

return

%----------------------------------
% ---- SUBROUTINES
%----------------------------------
%----------------------------------
function h = unfoldPlotL1Mesh(x,layer1Glocs2d)
h = figure;
gplot(x,layer1Glocs2d);
title('L1 gray mesh'); zoom on;

return;

%----------------------------------
function unfoldPlotgLocs2d(numGrayLayers,layerGlocs2d)
%
nFigsAcross=ceil(sqrt(numGrayLayers));
nFigsDown=ceil(sqrt(numGrayLayers));
for t=1:numGrayLayers
    if (t<=numGrayLayers)
        subplot(nFigsDown,nFigsAcross,t);

        plot(layerGlocs2d{t}(:,1),layerGlocs2d{t}(:,2),'.');
        axis equal;
    end % end check on current layer index
end % Next image
return;


function output = calllpsolve(interfacedata)

% Retrieve needed data
options = interfacedata.options;
F_struc = interfacedata.F_struc;
c       = interfacedata.c;
K       = interfacedata.K;
integer_variables = interfacedata.integer_variables;
binary_variables = interfacedata.binary_variables;
semicont_variables = interfacedata.semicont_variables;
ub      = interfacedata.ub;
lb      = interfacedata.lb;

n = length(c);
% Bounded variables converted to constraints
if ~isempty(ub)
    LB = lb;
    UB = ub;
else
    if isempty(integer_variables) & isempty(binary_variables)
        LB = -ones(n,1)*inf;;
        UB = ones(n,1)*inf;;
    else
        %LP_SOLVE FAILS IF BOUNDS NOT EXPLICIT
        [LB,UB,used_rows] = find_lp_bounds(F_struc,K);
        LB(isinf(LB)) = -1e12;
        UB(isinf(UB)) = 1e12;
        F_struc(K.f+used_rows,:)=[];
        K.l = K.l - length(used_rows);
        LB(binary_variables) = max(LB(binary_variables),0);
        UB(binary_variables) = min(UB(binary_variables),0);
    end
end

if options.showprogress;showprogress('Calling LPSOLVE',options.showprogress);end

f = - full(c);            % Must be full
A = - F_struc(:,2:end);
b = full(F_struc(:,1));   % Must be full
e = -ones(size(A,1),1);
e(1:K.f) = 0;

xint = uniquestripped([integer_variables binary_variables]);

% Call mex-interface
solvertime = clock;

if K.f>0
    Aeq = A(1:K.f,:);
    beq = b(1:K.f);
    Alin = A(K.f+1:end,:);
    blin = b(K.f+1:end);
    [LB,UB,Alin,blin] = remove_bounds_from_Ab(Alin,blin,LB,UB);
    A = [Aeq;Alin];
    b = [beq;blin];
    e = -ones(size(A,1),1);
    e(1:K.f) = 0;
    options.saveduals = 0;
else
    [LB,UB,A,b] = remove_bounds_from_Ab(A,b,LB,UB);
    e = -ones(size(A,1),1);
    options.saveduals = 0;
end
if ~isempty(semicont_variables)
    redundant = find(LB<=0 & UB>=0);
    semicont_variables = setdiff(semicont_variables,redundant);
end

% LPSOLVE assumes semi-continuous variables only can take negative values so
% we negate semi-continuous violating this
NegativeSemiVar = [];
if ~isempty(semicont_variables)
    NegativeSemiVar = find(UB(semicont_variables) < 0);
    if ~isempty(NegativeSemiVar)
        temp = UB(semicont_variables(NegativeSemiVar));
        UB(semicont_variables(NegativeSemiVar)) = -LB(semicont_variables(NegativeSemiVar));
        LB(semicont_variables(NegativeSemiVar)) = -temp;
        A(:,semicont_variables(NegativeSemiVar)) = -A(:,semicont_variables(NegativeSemiVar));
        f(semicont_variables(NegativeSemiVar)) = -f(semicont_variables(NegativeSemiVar));
    end
end
if options.savedebug
    save lpsolvedebug f A b e UB LB xint
end

lp = create_lp_solve_model(A,b,f,xint,LB,UB,e,options);
if ~isempty(K.sos)
    for i = 1:length(K.sos.type)
       lp_solve('add_SOS', lp, 'Dummy', str2num(K.sos.type(i)), 1, K.sos.variables{i}, K.sos.weight{i});
    end
end
if ~isempty(semicont_variables)
    lp_solve('set_semicont', lp, semicont_variables)
end

try
    if options.showprogress;showprogress(['Calling ' interfacedata.solver.tag],options.showprogress);end
    solvertime = tic;
    result=lp_solve('solve', lp);
    solvertime = toc(solvertime);
    if result == 0 | result == 1 | result == 11 | result == 12
        [obj, x, duals] = lp_solve('get_solution', lp);
    else
        obj = [];
        x = zeros(length(c),1);
        duals = [];
    end
    lp_solve('delete_lp', lp);
catch
    obj = [];
    x = zeros(length(c),1);
    duals = [];
    result = -1;
    lp_solve('delete_lp', lp);
end

% LPSOLVE assumes semi-continuous variables only can take negative values so
% we negate semi-continuous violating this
 if length(x) == length(c)
     if ~isempty(NegativeSemiVar)
        x(NegativeSemiVar) = -x(NegativeSemiVar);
     end
 end

if options.saveduals & isempty(integer_variables)
    D_struc = duals;
else
    D_struc = [];
end

switch result
    case {0,1}
        problem = 0; % OPTIMAL
    case 2
        problem = 1; % INFEASIBLE
    case 3
        problem = 2; % UNBOUNDED
    case {7,12,13}
        problem = 3; % RUN OUT OF TIME OR SIMILIAR
    case 5
        problem = 4;
    case {-2,10,11}
        problem = 11;
    otherwise
        problem = -1;
end

% Save all data sent to solver?
if options.savesolverinput
	solverinput.A = A;
	solverinput.f = f;
	solverinput.b = b;
	solverinput.LB = LB;
	solverinput.UB = UB;
    solverinput.xint = xint;
else
	solverinput = [];
end

% Save all data from the solver?
if options.savesolveroutput
	solveroutput.x = x;
    solveroutput.obj = obj;
    solveroutput.duals = duals;
    solveroutput.result = result;
else
	solveroutput = [];
end

% Standard interface
output = createOutputStructure(x,D_struc,[],problem,interfacedata.solver.tag,solverinput,solveroutput,solvertime);


function errores = errors

source = evalin('base','source');
decout = evalin('base','decout');
N = evalin('base','N');

errores = sum(abs(source - decout));


function hdr = ReadEfileHeader(EfileName)
% header = ReadEfileHeader(EfileName)
%
% Returns header info from Efile produced by G. Glover's auto-recon program.
% Output is structure having field names corresponding to Glover's parameters.
%
% Ress 9/01
% $Author: sayres $
% $Date: 2005/08/23 08:06:25 $
if nargin==0
    if exist('Raw/Pfiles','dir')
        startDir = 'Raw/Pfiles';
    else
        startDir = pwd;
    end
    [f p] = myUiGetfile(startDir,'*.7','Choose an E-file');
    EfileName = fullfile(p,f);
end

hdr = [];

fid = fopen(EfileName);
if fid == -1
    Alert(['Could not open file: ' EfileName])
    return
end

nLine = 0;
while 1
    line = fgetl(fid);
    if ~ischar(line), break, end
    ieq = findstr(line, '=');
    lhs = line(1:ieq-2);
    rhs = line(ieq+2:end);
    switch lhs
    case 'rev'
        % Revision
        hdr.rev = rhs;
    case 'date of scan'
        % Date
        hdr.date = rhs;
        % Make this more reasonable
        hdr.date=[hdr.date(1:6),hdr.date(8:end)];
    case 'time of scan'
        % Time
        hdr.time = rhs;
    case 'patient name'
        % Name
        hdr.name = rhs;

    case 'psd'
        % PSD
        hdr.psd = rhs;
    case 'coil'
        % Coil
        hdr.coil = rhs;
    case 'slquant'
        % Number of slices
        hdr.slquant = getNumberFromString(rhs);
    case 'num time frames'
        % Number of frames
        hdr.nframes = getNumberFromString(rhs);
    case 'numextra discards'
        % Number of discarded shots
        hdr.nextra = getNumberFromString(rhs);
    case 'nshot'
        % Number of interleaves
        hdr.nshots = getNumberFromString(rhs);
    case 'FOV'
        % Field of view (mm)
        hdr.FOV = getNumberFromString(rhs);
    case 'slice thick'
        % Slice thickness (mm)
        hdr.sliceThickness = getNumberFromString(rhs);
    case 'skip'
        % Slice spacing (skip)
        hdr.skip = getNumberFromString(rhs);
    case 'TR'
        % Echo time, TR (ms)
        hdr.TR = getNumberFromString(rhs);
    case 'TE'
        % Repetition time, TE (ms)
        hdr.TE = getNumberFromString(rhs);
    case 'time/frame'
        % Acquisition time (ms)
        hdr.tAcq = getNumberFromString(rhs);
    case 'equiv matrix size'
        % Equivalent matrix size
        hdr.equivMatSize = getNumberFromString(rhs);
    case 'imgsize'
        % Image size
        hdr.imgsize = getNumberFromString(rhs);
    case 'pixel size'
        % Pixel size (mm)
        hdr.pixel = getNumberFromString(rhs);
    case 'freq'
        % Frequency (MHz)
        hdr.freq = getNumberFromString(rhs)/1.e6;
    case 'R1, R2, TG (mps)'
        % Gains [R1, R2, TG]
        hdr.R1 = getNumberFromString(rhs, 1);
        hdr.R2 = getNumberFromString(rhs, 2);
        hdr.TG = getNumberFromString(rhs, 3);
    otherwise
    end
end


function script_rfcn_VOC0712_ResNet101_OHEM_ss()
% script_rfcn_VOC0712_ResNet101_OHEM_ss()
% RFCN training and testing with OHEM using ResNet101 model and selective
% search proposals
% --------------------------------------------------------
% R-FCN implementation
% Modified from MATLAB Faster R-CNN (https://github.com/shaoqingren/faster_rcnn)
% Copyright (c) 2016, Jifeng Dai
% Licensed under The MIT License [see LICENSE for details]
% --------------------------------------------------------

clc;
clear mex;
clear is_valid_handle; % to clear init_key
run(fullfile(fileparts(fileparts(mfilename('fullpath'))), 'startup'));
%% -------------------- CONFIG --------------------
opts.caffe_version          = 'caffe_rfcn';
opts.gpu_id                 = auto_select_gpu;
active_caffe_mex(opts.gpu_id, opts.caffe_version);

% model
model                       = Model.ResNet101_for_RFCN_VOC0712_OHEM();
% cache name
opts.cache_name             = 'rfcn_VOC0712_ResNet101_OHEM_ss';
% config
conf                        = rfcn_config_ohem('image_means', model.mean_image);
% train/test data
fprintf('Loading dataset...')
dataset                     = [];
dataset                     = Dataset.voc0712_trainval_ss(dataset, 'train', conf.use_flipped);
dataset                     = Dataset.voc2007_test_ss(dataset, 'test', false);
fprintf('Done.\n');

% do validation, or not
opts.do_val                 = true;

%% -------------------- TRAINING --------------------

opts.rfcn_model        = rfcn_train(conf, dataset.imdb_train, dataset.roidb_train, ...
                                'do_val',           opts.do_val, ...
                                'imdb_val',         dataset.imdb_test, ...
                                'roidb_val',        dataset.roidb_test, ...
                                'solver_def_file',  model.solver_def_file, ...
                                'net_file',         model.net_file, ...
                                'cache_name',       opts.cache_name, ...
                                'caffe_version',    opts.caffe_version);
assert(exist(opts.rfcn_model, 'file') ~= 0, 'not found trained model');

%% -------------------- TESTING --------------------
                          rfcn_test(conf, dataset.imdb_test, dataset.roidb_test, ...
                                'net_def_file',     model.test_net_def_file, ...
                                'net_file',         opts.rfcn_model, ...
                                'cache_name',       opts.cache_name,...
                                'ignore_cache',     true);

end


function [epochs] = fiff_read_epochs(fname)
%
% [epochs] = fiff_read_epochs(fname,setno)
%
% Read eochs from file
%
%
%   Author : Martin Luessi, MGH Martinos Center
%   License : BSD 3-clause

global FIFF;
if isempty(FIFF)
    FIFF = fiff_define_constants();
end

me='MNE:fiff_read_epochs';

%
%   Open the file
%
fprintf(1,'Reading %s ...\n',fname);
[ fid, tree ] = fiff_open(fname);
%
%   Read the measurement info
%
[ info, meas ] = fiff_read_meas_info(fid,tree);
info.filename = fname;

%
%   Read events
%
[ events, mappings ] = fiff_read_events(fid,tree);

%
% Locate the data of interest
%
processed = fiff_dir_tree_find(meas, FIFF.FIFFB_PROCESSED_DATA);
if length(processed) == 0
        fclose(fid);
    error(me,'Could not find epochs data');
end

ep = fiff_dir_tree_find(meas, FIFF.FIFFB_MNE_EPOCHS);
if length(ep) == 0
        fclose(fid);
    error(me,'Could not find epochs data');
end

comment = '';
selection = '';
drop_log = '';
for k = 1:ep.nent
    kind = ep.dir(k).kind;
    pos  = ep.dir(k).pos;
    switch kind
        case FIFF.FIFF_FIRST_SAMPLE
            tag = fiff_read_tag(fid,pos);
            first = tag.data;
        case FIFF.FIFF_LAST_SAMPLE
            tag = fiff_read_tag(fid,pos);
            last = tag.data;
        case FIFF.FIFF_COMMENT
            tag = fiff_read_tag(fid,pos);
            comment = tag.data;
        case FIFF.FIFF_EPOCH
            tag = fiff_read_tag(fid,pos);
            epoch = tag.data;
        case FIFF.FIFF_MNE_BASELINE_MIN
            tag = fiff_read_tag(fid,pos);
            bmin = tag.data;
        case FIFF.FIFF_MNE_BASELINE_MAX
            tag = fiff_read_tag(fid,pos);
            bmax = tag.data;
        case FIFF.FIFFB_MNE_EPOCHS_SELECTION
            tag = fiff_read_tag(fid,pos);
            selection = tag.data;
        case FIFF.FIFFB_MNE_EPOCHS_DROP_LOG
            tag = fiff_read_tag(fid,pos);
            drop_log = tag.data;
    end
end

if ~exist('epoch','var')
    fclose(fid);
    error(me,'Epochs data not found');
end

if ~exist('bmin','var')
    bmin = double(first)/info.sfreq;
end

if ~exist('bmax','var')
    bmax = double(last)/info.sfreq;
end

baseline = [bmin, bmax];

nsamp = last-first+1;
fprintf(1,'\tFound the data of interest:\n');
fprintf(1,'\t\tt = %10.2f ... %10.2f ms (%s)\n',...
    1000*double(first)/info.sfreq,1000*double(last)/info.sfreq,comment);
if ~isempty(info.comps)
    fprintf(1,'\t\t%d CTF compensation matrices available\n',length(info.comps));
end

if size(epoch,1) ~= size(events,1)
    fclose(fid);
    error(me,'Incorrect number of trials (%d instead of %d)',...
        size(epoch,1),size(events,1));
end

if size(epoch,2) ~= info.nchan
    fclose(fid);
    error(me,'Incorrect number of channels (%d instead of %d)',...
        size(epoch,2),info.nchan);
end

if size(epoch,3) ~= nsamp
    fclose(fid);
    error(me,'Incorrect number of samples (%d instead of %d)',...
        size(epoch,3),nsamp);
end

%
%   Calibrate
%
for k = 1:info.nchan
    cals(k) = info.chs(k).cal;
end

nepochs = size(epoch, 1);
epoch = repmat(cals, [nepochs, 1, nsamp]) .* epoch;

times = double(first:last) / info.sfreq;
tmin = times(1);
tmax = times(end);

%
% Put it all together
%
epochs.info = info;
epochs.events = events;
epochs.name = comment;
epochs.times = times;
epochs.tmin = tmin;
epochs.tmax = tmax;
epochs.data = epoch;
epochs.baseline = baseline;
epochs.event_id = mappings;
epochs.selection = selection;
epochs.drop_log = drop_log;

fclose(fid);

return;


function allMets = evaluateTracking(allSeq, resDir, dataDir)
%% evaluate CLEAR MOT and other metrics
% concatenate ALL sequences and evaluate as one!
%
% SETUP:
%
% define directories for tracking results...
% resDir = fullfile('res','data',filesep);
% ... and the actual sequences
% dataDir = fullfile('..','data','2DMOT2015','train',filesep);
%
%

fprintf('Sequences: \n');
disp(allSeq')

% concat gtInfo
gtInfo=[];
gtInfo.Xi=[];
allFgt=zeros(1,length(allSeq));

% Find out the length of each sequence
% and concatenate ground truth
gtInfoSingle=[];
seqCnt=0;
for s=allSeq
    seqCnt=seqCnt+1;
    seqName = char(s);
    seqFolder= [dataDir,seqName,filesep];

    assert(isdir(seqFolder),'Sequence folder %s missing',seqFolder);

    gtFile = fullfile(dataDir,seqName,'gt','gt.txt');
    gtI = convertTXTToStruct(gtFile,seqFolder);

    [Fgt,Ngt] = size(gtInfo.Xi);
    [FgtI,NgtI] = size(gtI.Xi);
    newFgt = Fgt+1:Fgt+FgtI;
    newNgt = Ngt+1:Ngt+NgtI;

    gtInfo.Xi(newFgt,newNgt) = gtI.Xi;
    gtInfo.Yi(newFgt,newNgt) = gtI.Yi;
    gtInfo.W(newFgt,newNgt) = gtI.W;
    gtInfo.H(newFgt,newNgt) = gtI.H;

    gtInfoSingle(seqCnt).wc=0;

    % fill in world coordinates if they exist
    if isfield(gtI,'Xgp') && isfield(gtI,'Ygp')
        gtInfo.Xgp(newFgt,newNgt) = gtI.Xgp;
        gtInfo.Ygp(newFgt,newNgt) = gtI.Ygp;
        gtInfoSingle(seqCnt).wc=1;
    end

    allFgt(seqCnt) = FgtI;

    gtInfoSingle(seqCnt).gtInfo=gtI;

end
gtInfo.frameNums=1:size(gtInfo.Xi,1);

allMets=[];

mcnt=1;

fprintf('Evaluating ... \n');

clear stInfo
stInfo.Xi=[];

evalMethod=1;

% flags for entire benchmark
% if one seq missing, evaluation impossible
eval2D=1;
eval3D=1;

seqCnt=0;

% iterate over each sequence
for s=allSeq

    seqCnt=seqCnt+1;
    seqName = char(s);

    fprintf('\t... %s\n',seqName);

    % if a result is missing, we cannot evaluate this tracker
    resFile = fullfile(resDir,[seqName '.txt']);
    if ~exist(resFile,'file')
        fprintf('WARNING: result for %s not available!\n',seqName);
        eval2D=0;
        eval3D=0;
        continue;
    end

    stI = convertTXTToStruct(resFile);
%     stI.Xi(find(stI.Xi(:)))=-1;
    % check if bounding boxes available in solution
    imCoord=1;
    if all(stI.Xi(find(stI.Xi(:)))==-1)
        imCoord=0;
    end

    worldCoordST=0; % state
    if isfield(stI,'Xgp') && isfield(stI,'Ygp')
        worldCoordST=1;
    end

    [FI,NI] = size(stI.Xi);

    % if stateInfo shorter, pad with zeros
    % GT and result must be equal length
    if FI<allFgt(seqCnt)
        missingFrames = FI+1:allFgt(seqCnt);
        stI.Xi(missingFrames,:)=0;
        stI.Yi(missingFrames,:)=0;
        stI.W(missingFrames,:)=0;
        stI.H(missingFrames,:)=0;
        if worldCoordST
            stI.Xgp(missingFrames,:)=0; stI.Ygp(missingFrames,:)=0;
        end
        [FI,NI] = size(stI.Xi);

    end

    % get result for one sequence only

    [mets, mInf]=CLEAR_MOT_HUN(gtInfoSingle(seqCnt).gtInfo,stI);

    allMets(mcnt).mets2d(seqCnt).name=seqName;
    allMets(mcnt).mets2d(seqCnt).m=mets;

    allMets(mcnt).mets3d(seqCnt).name=seqName;
    allMets(mcnt).mets3d(seqCnt).m=zeros(1,length(mets));

    if imCoord
        fprintf('*** 2D (Bounding Box overlap) ***\n'); printMetrics(mets); fprintf('\n');
    else
        fprintf('*** Bounding boxes not available ***\n\n');
        eval2D=0;
    end

    % if world coordinates available, evaluate in 3D
    if  gtInfoSingle(seqCnt).wc &&  worldCoordST
        evopt.eval3d=1;evopt.td=1;
        [mets, mInf]=CLEAR_MOT_HUN(gtInfoSingle(seqCnt).gtInfo,stI,evopt);
            allMets(mcnt).mets3d(seqCnt).m=mets;
        fprintf('*** 3D (in world coordinates) ***\n'); printMetrics(mets); fprintf('\n');
    else
        eval3D=0;
    end

    [F,N] = size(stInfo.Xi);
    newF = F+1:F+FI;
    newN = N+1:N+NI;

    % concat result
    stInfo.Xi(newF,newN) = stI.Xi;
    stInfo.Yi(newF,newN) = stI.Yi;
    stInfo.W(newF,newN) = stI.W;
    stInfo.H(newF,newN) = stI.H;
    if isfield(stI,'Xgp') && isfield(stI,'Ygp')
        stInfo.Xgp(newF,newN) = stI.Xgp;stInfo.Ygp(newF,newN) = stI.Ygp;
    end

end
stInfo.frameNums=1:size(stInfo.Xi,1);

if eval2D
    fprintf('\n');
    fprintf(' ********************* Your Benchmark Results (2D) ***********************\n');

    [m2d, mInf]=CLEAR_MOT_HUN(gtInfo,stInfo);
    allMets.bmark2d=m2d;

    evalFile = fullfile(resDir, 'eval2D.txt');

    printMetrics(m2d);
    dlmwrite(evalFile,m2d);
end

if eval3D
    fprintf('\n');
    fprintf(' ********************* Your Benchmark Results (3D) ***********************\n');

    evopt.eval3d=1;evopt.td=1;

    [m3d, mInf]=CLEAR_MOT_HUN(gtInfo,stInfo,evopt);
    allMets.bmark3d=m3d;

    evalFile = fullfile(resDir, 'eval3D.txt');

    printMetrics(m3d);
    dlmwrite(evalFile,m3d);
end
if ~eval2D && ~eval3D
    fprintf('ERROR: results cannot be evaluated\n');
end


function [hashVal,hexVal]=genHashVal(inputArray,algorithm)
%%GENHASHVAL  Get a hash value of a character string or a 1D array of
%             bytes. That is, integer values from 0 through 255. The hash
%             value can either be a simple integer value, useful for quick
%             comparison of strings, or one can get a cryptographic hash as
%             an an array of integers from 0 to 255 using various
%             algorithms. Cryptographic hashes are also returned has
%             hexadecimal strings.
%
%INPUTS: inputArray A 1-dimensional string of characters or array of
%                   integer values from 0 to 255 for which one desired a
%                   hash code.
%        algorithm  An optional  string specifying the algorithm to use to
%                   generate the hash. Possible values are
%                   'simple' (the default) Use a simple, non-cryptographic
%                            hash function to get a single integer hash for
%                            the input.
%                   'MD2'    Use the MD2 algorithm to get a weak
%                            cryptographic hash, along with its hexidecimal
%                            string.
%                   'MD5'    Use the MD5 algorithm to get a weak
%                            cryptographic hash, along with its hexidecimal
%                            string.
%                   'SHA-1'  Use the SHA-1 algorithm to get a cryptographic
%                            hash along with its hexidecimal string.
%                  'SHA-384' Use the SHA-384 algorithm to get a
%                            cryptographic hash along with its hexidecimal
%                            string.
%                  'SHA-256' Use the SHA-256 algorithm to get a
%                            cryptographic hash along with its hexidecimal
%                            string.
%                  'SHA-512' Use the SHA-512 algorithm to get a
%                            cryptographic hash along with its hexidecimal
%                            string.
%
%OUTPUTS: hashVal  If algorithmis 'simple', then this is an integer hash
%                  code for the given input. Otherwise, it is an array of
%                  bytes representign the has value.
%         hexVal   A hexidecimal string representing the hash value.
%
%The simple algorithm is executed using the hashCode method in Java's
%java.lang.String class. The cryptographic hashes are executed using the
%java.security.MessageDigest class. In Matlab, one can directly access Java
%functions, which is what was done to implement this function.
%
%April 2014 David F. Crouse, Naval Research Laboratory, Washington D.C.
%(UNCLASSIFIED) DISTRIBUTION STATEMENT A. Approved for public release.

if(~ischar(inputArray)&&any(inputArray~=fix(inputArray))||any(inputArray>255)||any(inputArray<0))
    error('Hash values can only be obtained from character arrays or arrays of positive integer values from 0 through 255');
end

if(nargin<2)
    algorithm='simple';
end

%If the simple aglorithm is chosen. otherwise, a cryptographic has is being
%used.
if(strcmp(algorithm,'simple'))
    %Convert the array to a character string.
    if(~ischar(inputArray))
        inputArray=native2unicode(inputArray);
    end

    myString=java.lang.String(inputArray);
    hashVal=myString.hashCode;
    %Java's integers are always 32-bits.
    hexVal=dec2hex(typecast(int32(hashVal),'uint32'),8);
    return;
end

%The cryptographic has functions work on bytes, not strings.
if(ischar(inputArray))
    inputArray=unicode2native(inputArray);
end

myDigest=java.security.MessageDigest.getInstance(algorithm);

myDigest.update(inputArray);
hashVal=myDigest.digest;
hexVal=javax.xml.bind.DatatypeConverter.printHexBinary(hashVal);
end

%LICENSE:
%
%The source code is in the public domain and not licensed or under
%copyright. The information and software may be used freely by the public.
%As required by 17 U.S.C. 403, third parties producing copyrighted works
%consisting predominantly of the material produced by U.S. government
%agencies must provide notice with such work(s) identifying the U.S.
%Government material incorporated and stating that such material is not
%subject to copyright protection.
%
%Derived works shall not identify themselves in a manner that implies an
%endorsement by or an affiliation with the Naval Research Laboratory.
%
%RECIPIENT BEARS ALL RISK RELATING TO QUALITY AND PERFORMANCE OF THE
%SOFTWARE AND ANY RELATED MATERIALS, AND AGREES TO INDEMNIFY THE NAVAL
%RESEARCH LABORATORY FOR ALL THIRD-PARTY CLAIMS RESULTING FROM THE ACTIONS
%OF RECIPIENT IN THE USE OF THE SOFTWARE.


classdef ObjectnessBING < handle
    %OBJECTNESSBING  The Binarized normed gradients algorithm for Objectness
    %
    % Implementation of BING for Objectness.
    %
    % ## Saliency API
    %
    % Many computer vision applications may benefit from understanding where
    % humans focus given a scene. Other than cognitively understanding the way
    % human perceive images and scenes, finding salient regions and objects in
    % the images helps various tasks such as speeding up object detection,
    % object recognition, object tracking and content-aware image editing.
    %
    % About the saliency, there is a rich literature but the development is
    % very fragmented. The principal purpose of this API is to give a unique
    % interface, a unique framework for use and plug sever saliency
    % algorithms, also with very different nature and methodology, but they
    % share the same purpose, organizing algorithms into three main
    % categories:
    %
    % * Static Saliency
    % * Motion Saliency
    % * Objectness
    %
    % Saliency UML diagram:
    %
    % ![image](https://docs.opencv.org/3.3.1/saliency.png)
    %
    % To see how API works, try tracker demo: `computeSaliency_demo.m`.
    %
    % ## Objectness Algorithms
    %
    % Objectness is usually represented as a value which reflects how likely
    % an image window covers an object of any category. Algorithms belonging
    % to this category, avoid making decisions early on, by proposing a small
    % number of category-independent proposals, that are expected to cover all
    % objects in an image. Being able to perceive objects before identifying
    % them is closely related to bottom up visual attention (saliency).
    %
    % Presently, the Binarized normed gradients algorithm [BING] has been
    % implemented.
    %
    % ## References
    % [BING]:
    % > Cheng, Ming-Ming, et al. "BING: Binarized normed gradients for
    % > objectness estimation at 300fps". In IEEE CVPR, 2014.
    %
    % See also: cv.StaticSaliencySpectralResidual,
    %  cv.MotionSaliencyBinWangApr2014
    %

    properties (SetAccess = private)
        % Object ID
        id
    end

    properties (Dependent)
        % base for window size quantization. default 2
        Base
        % Size for non-maximal suppress. default 2
        NSS
        % As described in the paper: feature window size (W, W). default 8
        W
    end

    methods
        function this = ObjectnessBING()
            %OBJECTNESSBING  Constructor, creates a specialized saliency algorithm of this type
            %
            %     obj = cv.ObjectnessBING()
            %
            % See also: cv.ObjectnessBING.computeSaliency
            %
            this.id = ObjectnessBING_(0, 'new');
        end

        function delete(this)
            %DELETE  Destructor
            %
            %     obj.delete()
            %
            % See also: cv.ObjectnessBING
            %
            if isempty(this.id), return; end
            ObjectnessBING_(this.id, 'delete');
        end

        function objectnessValues = getObjectnessValues(this)
            %GETOBJECTNESSVALUES  Return the list of the rectangles' objectness value
            %
            %     objectnessValues = obj.getObjectnessValues()
            %
            % ## Output
            % * __objectnessValues__ vector of floats in the same order as
            %   `objectnessBoundingBox` returned by the algorithm (in
            %   `computeSaliency` function). The bigger value these scores
            %   are, it is more likely to be an object window.
            %
            % See also: cv.ObjectnessBING.computeSaliency
            %
            objectnessValues = ObjectnessBING_(this.id, 'getobjectnessValues');
        end

        function setTrainingPath(this, trainingPath)
            %SETTRAININGPATH  This is a utility function that allows to set the correct path from which the algorithm will load the trained model
            %
            %     obj.setTrainingPath(trainingPath)
            %
            % ## Input
            % * __trainingPath__ trained model path.
            %
            % See also: cv.ObjectnessBING.setBBResDir
            %
            ObjectnessBING_(this.id, 'setTrainingPath', trainingPath);
        end

        function setBBResDir(this, resultsDir)
            %SETBBRESDIR  This is a utility function that allows to set an arbitrary path in which the algorithm will save the optional results
            %
            %     obj.setBBResDir(resultsDir)
            %
            % ## Input
            % * __resultsDir__ results' folder path.
            %
            % Writes on file the total number and the list of rectangles
            % returned by objectness, one for each row.
            %
            % See also: cv.ObjectnessBING.setTrainingPath
            %
            ObjectnessBING_(this.id, 'setBBResDir', resultsDir);
        end
    end

    %% Algorithm
    methods
        function clear(this)
            %CLEAR  Clears the algorithm state
            %
            %     obj.clear()
            %
            % See also: cv.ObjectnessBING.empty, cv.ObjectnessBING.load
            %
            ObjectnessBING_(this.id, 'clear');
        end

        function b = empty(this)
            %EMPTY  Checks if detector object is empty
            %
            %     b = obj.empty()
            %
            % ## Output
            % * __b__ Returns true if the detector object is empty (e.g in the
            %   very beginning or after unsuccessful read).
            %
            % See also: cv.ObjectnessBING.clear, cv.ObjectnessBING.load
            %
            b = ObjectnessBING_(this.id, 'empty');
        end

        function save(this, filename)
            %SAVE  Saves the algorithm parameters to a file
            %
            %     obj.save(filename)
            %
            % ## Input
            % * __filename__ Name of the file to save to.
            %
            % This method stores the algorithm parameters in the specified
            % XML or YAML file.
            %
            % See also: cv.ObjectnessBING.load
            %
            ObjectnessBING_(this.id, 'save', filename);
        end

        function load(this, fname_or_str, varargin)
            %LOAD  Loads algorithm from a file or a string
            %
            %     obj.load(fname)
            %     obj.load(str, 'FromString',true)
            %     obj.load(..., 'OptionName',optionValue, ...)
            %
            % ## Input
            % * __fname__ Name of the file to read.
            % * __str__ String containing the serialized model you want to
            %   load.
            %
            % ## Options
            % * __ObjName__ The optional name of the node to read (if empty,
            %   the first top-level node will be used). default empty
            % * __FromString__ Logical flag to indicate whether the input is a
            %   filename or a string containing the serialized model.
            %   default false
            %
            % This method reads algorithm parameters from the specified XML or
            % YAML file (either from disk or serialized string). The previous
            % algorithm state is discarded.
            %
            % See also: cv.ObjectnessBING.save
            %
            ObjectnessBING_(this.id, 'load', fname_or_str, varargin{:});
        end

        function name = getDefaultName(this)
            %GETDEFAULTNAME  Returns the algorithm string identifier
            %
            %     name = obj.getDefaultName()
            %
            % ## Output
            % * __name__ This string is used as top level XML/YML node tag
            %   when the object is saved to a file or string.
            %
            % See also: cv.ObjectnessBING.save, cv.ObjectnessBING.load
            %
            name = ObjectnessBING_(this.id, 'getDefaultName');
        end
    end

    %% Saliency, Objectness
    methods
        function objectnessBoundingBox = computeSaliency(this, img)
            %COMPUTESALIENCY  Compute the saliency
            %
            %     objectnessBoundingBox = obj.computeSaliency(img)
            %
            % ## Input
            % * __img__ The input image, 8-bit color.
            %
            % ## Output
            % * __objectnessBoundingBox__ objectness Bounding Box vector.
            %   According to the result given by this specialized algorithm,
            %   the `objectnessBoundingBox` is a cell array of 4-element
            %   vectors. Each bounding box is represented by
            %   `[minX, minY, maxX, maxY]`.
            %
            % Performs all the operations and calls all internal functions
            % necessary for the accomplishment of the Binarized normed
            % gradients algorithm.
            %
            % See also: cv.ObjectnessBING.ObjectnessBING
            %
            objectnessBoundingBox = ObjectnessBING_(this.id, 'computeSaliency', img);
        end
    end

    %% Getters/Setters
    methods
        function value = get.Base(this)
            value = ObjectnessBING_(this.id, 'get', 'Base');
        end
        function set.Base(this, value)
            ObjectnessBING_(this.id, 'set', 'Base', value);
        end

        function value = get.NSS(this)
            value = ObjectnessBING_(this.id, 'get', 'NSS');
        end
        function set.NSS(this, value)
            ObjectnessBING_(this.id, 'set', 'NSS', value);
        end

        function value = get.W(this)
            value = ObjectnessBING_(this.id, 'get', 'W');
        end
        function set.W(this, value)
            ObjectnessBING_(this.id, 'set', 'W', value);
        end
    end

end


function PPI = spm_peb_ppi(varargin)
% Bold deconvolution to create physio- or psycho-physiologic interactions
% FORMAT PPI = spm_peb_ppi(SPMname,ppiflag,VOI,Uu,ppiname,showGraphics)
%
% SPM          - Structure containing generic details about the analysis or
%                the fully qualified filename of such a structure.
% ppiflag      - Type of analysis. Must be one of:
%                  'simple deconvolution'          or 'sd'
%                  'psychophysiologic interaction' or 'ppi'
%                  'physiophysiologic interaction' or 'phipi'
% VOI          - Structure containing details about a VOI (as produced by
%                spm_regions) or the fully qualified filename of such a
%                structure. If a structure, then VOI should be of size 1x1
%                in the case of simple deconvolution, and psychophysiologic
%                interactions) or 1x2, in the case of physiophysiologic
%                interactions. If a file name it should be 1xN or 2xN.
% Uu           - Matrix of input variables and contrast weights. This is an
%                [n x 3] matrix. The first column indexes SPM.Sess.U(i). The
%                second column indexes the name of the input or cause, see
%                SPM.Sess.U(i).name{j}. The third column is the contrast
%                weight. Unless there are parametric effects the second
%                column will generally be a 1.
% ppiname      - Basename of the PPI file to save. The saved file will be:
%                <PATH_TO_SPM.MAT>/PPI_<ppiname>.mat
% showGraphics - empty or 1 = yes, 0 = no.
%
%
% PPI.ppi      - (PSY*xn  or xn1*xn2) convolved with the HRF
% PPI.Y        - Original BOLD eigenvariate. Use as covariate of no interest
% PPI.P        - PSY convolved with HRF for psychophysiologic interactions,
%                or in the case of physiophysologic interactions contains
%                the eigenvariate of the second region.
% PPI.name     - Name of PPI
% PPI.xY       - Original VOI information
% PPI.xn       - Deconvolved neural signal(s)
% PPI.psy.u    - Psychological variable or input function (PPIs only)
% PPI.psy.w    - Contrast weights for psychological variable (PPIs only)
% PPI.psy.name - Names of psychological conditions (PPIs only)
%__________________________________________________________________________
%
% This routine is effectively a hemodynamic deconvolution using full priors
% and EM to deconvolve the HRF from a hemodynamic time series to give a
% neuronal time series [that can be found in PPI.xn].  This deconvolution
% conforms to Wiener filtering. The neuronal process is then used to form
% PPIs. See help text within function for more details.
%__________________________________________________________________________
% Copyright (C) 2002-2014 Wellcome Trust Centre for Neuroimaging

% Darren Gitelman
% $Id: spm_peb_ppi.m 6556 2015-09-15 15:42:04Z guillaume $

% SETTING UP A PPI THAT ACCOUNTS FOR THE HRF
% =========================================================================
% PPI's were initially conceived as a means of identifying regions whose
% reponses can be explained in terms of an interaction between activity in
% a specified source (the physiological factor) and some experimental
% effect (the psychological factor). However, a problem in setting up PPI's
% is that in order to derive a proper estimate of the interaction between
% a psychological variable (P) and measured hemodynamic signal (x), one
% cannot simply convolve the psychological variable with the hrf (HRF) and
% multiply by the signal. Thus:
%
%                  conv(P,HRF).* x ~= conv((P.*xn),HRF)
%
% P   = psychological variable
% HRF = hemodynamic response function
% xn  = underlying neural signal which in fMRI is convolved with the hrf to
%       give the signal one measures -- x.
% x   = measured fmri signal
%
% It is actually the right hand side of the equation one wants.
% Thus one has to work backwards, in a sense, and deconvolve the hrf
% from x to get xn. This can then be multiplied by P and the resulting
% vector (or matrix) reconvolved with the hrf.
%
% This algorithm uses a least squares strategy to solve for xn.
%
% The source's hemodynamics are x = HRF*xn;
%
% Using the constraint that xn should have a uniform spectral density
% we can expand x in terms of a discrete cosine set (xb)
%
%      xn  = xb*B
%       B  = parameter estimate
%
% The estimator of x is then
%
%       x  = HRF(k,:)*xn
%       x  = HRF(k,:) * xb * B
%
% This accounts for different time resolutions between our hemodynamic
% signal and the discrete representation of the psychological variable. In
% this case k is a vector representing the time resolution of the scans.
%
% Conditional estimates of B allow for priors that ensure uniform variance
% over frequencies.
%
% PPI STATISTICAL MODEL
% =========================================================================
% Once the PPI.ppi interaction term has been calculated a new GLM must be
% setup to search for the interaction effects across the brain. This is
% done using a standard, first level, fMRI model, which must include 3
% covariates, PPI.ppi (interaction), PPI.Y (main effect: source region bold
% signal) and PPI.P (main effect: "psychological" condition), plus any
% nuisance regressors according to the particular design.
%
% NB: Designs that include only the interaction term without the main
% effects are not proper as inferences on the interaction will include a
% mixture of both main and interaction effects.
%
% Once the model has been setup and run, a contrast of [1 0 0] over the
% PPI.ppi, PPI.Y and PPI.P columns respectively, will show regions with a
% positive relationship to the interaction term, discounting any main
% effects. Negative regressions can be examined with [-1 0 0]. A PPI random
% effects analysis would involve taking the con* files from the [1 0 0]
% t-contrast for each subject and forwarding them to a second level
% analysis.

% Shortcut for PPI display
%--------------------------------------------------------------------------
if nargin && ischar(varargin{1}) && strcmpi(varargin{1},'display')
    if nargin > 1
        PPI = varargin{2};
    else
        [PPI, sts] = spm_select(1,'^PPI.*\.mat$');
        if ~sts, return; end
    end
    if ischar(PPI), load(PPI); end
    display_ppi(PPI);
    return;
end

% Set up the graphical interface
%--------------------------------------------------------------------------
Finter = spm_figure('FindWin','Interactive');
if ~nargin, Finter = spm_figure('GetWin','Interactive'); end
header = get(Finter,'Name');
spm_clf(Finter); set(Finter,'name','PPI Setup');

% Check inputs
%--------------------------------------------------------------------------
if nargin && isstruct(varargin{1})
    SPM = varargin{1};
    try, swd = SPM.pwd; catch, swd = pwd; end
else
    try
        P            = varargin{1};
    catch
        [P, sts]     = spm_select(1,'^SPM\.mat$','Select SPM.mat');
        if ~sts, PPI = []; return; end
    end
    swd = spm_file(P,'fpath');
    load(fullfile(swd,'SPM.mat'));
end
SPM.swd = spm_file(swd,'cpath');
cwd     = pwd;
cd(SPM.swd)

% Ask whether to perform physiophysiologic or psychophysiologic interactions
%--------------------------------------------------------------------------
try
    ppiflag = varargin{2};
catch
    ppiflag = {'simple deconvolution',...
               'psychophysiologic interaction',...
               'physiophysiologic interaction'};
    i       = spm_input('Analysis type?',1,'m',ppiflag);
    ppiflag = ppiflag{i};
end

switch lower(ppiflag)

    case  {'simple deconvolution','sd'}
    %======================================================================
    if nargin>2 && isstruct(varargin{3})
        p.xY = varargin{3};
    else
        try
            VOI = varargin{3};
            p   = load(deblank(VOI(1,:)),'xY');
        catch
            spm_input('physiological variable:...  ',2,'d');
            [VOI,sts] = spm_select(1,'^VOI.*\.mat$',{'select VOI'});
            if ~sts, PPI = []; return; end
            p   = load(VOI,'xY');
        end
    end
    xY(1) = p.xY;
    Sess  = SPM.Sess(xY(1).Sess);

    case  {'physiophysiologic interaction','phipi'}
    %======================================================================
    if nargin>2 && isstruct(varargin{3})
        xY = varargin{3};
        xY = xY(:)';
        if numel(xY) ~= 2
            error('Must include 2 VOI structures for physiophysiologic interactions')
        end
    else
        try
            VOI = varargin{3};
            if size(VOI,1) ~= 2
                error('Must include 2 VOI filenames for physiophysiologic interactions')
            end
            for i = 1:2
                p     = load(deblank(VOI(i,:)),'xY');
                xY(i) = p.xY;
            end
        catch
            spm_input('physiological variables:...  ',2,'d');
            [VOI,sts] = spm_select(2,'^VOI.*\.mat$',{'select VOIs'});
            if ~sts, PPI = []; return; end
            for i = 1:2
                p     = load(deblank(VOI(i,:)),'xY');
                xY(i) = p.xY;
            end
        end
    end
    Sess = SPM.Sess(xY(1).Sess);

    case  {'psychophysiologic interaction','ppi'}
    %======================================================================
    if nargin>2 && isstruct(varargin{3})
        p.xY = varargin{3};
    else
        try
            VOI = varargin{3};
            p   = load(deblank(VOI(1,:)),'xY');
        catch
            spm_input('physiological variable:...  ',2,'d');
            [VOI,sts] = spm_select(1,'^VOI.*\.mat$',{'select VOI'});
            if ~sts, PPI = []; return; end
            p   = load(VOI,'xY');
        end
    end
    xY(1) = p.xY;
    Sess  = SPM.Sess(xY(1).Sess);

    % get 'causes' or inputs U
    %----------------------------------------------------------------------
    U.name = {};
    U.u    = [];
    U.w    = [];
    try
        Uu = varargin{4};
        for i = 1:size(Uu,1)
            U.u           = [U.u Sess.U(Uu(i,1)).u(33:end,Uu(i,2))];
            U.name{end+1} = Sess.U(Uu(i,1)).name{Uu(i,2)};
            U.w           = [U.w Uu(i,3)];
        end
    catch
        spm_input('Psychological variable:...  ',2,'d');
        u      = length(Sess.U);
        for  i = 1:u
            for  j = 1:length(Sess.U(i).name)
                str   = ['include ' Sess.U(i).name{j} '?'];
                if spm_input(str,3,'y/n',[1 0])
                    str             = 'Contrast weight';
                    tmpw            = spm_input(str,4,'e',[],1);
                    % if tmpw==0 then don't include the column in the
                    % design. This takes care of the possibility that the
                    % user would select to include the column but then give
                    % it a 0 weight.
                    %------------------------------------------------------
                    if tmpw ~= 0
                        U.w             = [U.w tmpw];
                        U.u             = [U.u Sess.U(i).u(33:end,j)];
                        U.name{end + 1} = Sess.U(i).name{j};
                    end
                end
            end
        end
    end

    otherwise
    %======================================================================
    error('Unknown type of analysis');

end % (switch setup)

% Name of PPI file to be saved
%--------------------------------------------------------------------------
try
    PPI.name = varargin{5};
catch
    PPI.name = spm_input('Name of PPI',3,'s','PPI');
end

% Check if Graphical output should be shown
%--------------------------------------------------------------------------
try
    showGraphics = varargin{6};
catch
    showGraphics = 1;
end

% Setup variables
%--------------------------------------------------------------------------
RT      = SPM.xY.RT;
dt      = SPM.xBF.dt;
NT      = round(RT/dt);
fMRI_T0 = SPM.xBF.T0;
N       = length(xY(1).u);
k       = 1:NT:N*NT;                       % microtime to scan time indices

% Setup other output variables
%--------------------------------------------------------------------------
PPI.xY = xY;
PPI.RT = RT;
PPI.dt = dt;

% Create basis functions and hrf in scan time and microtime
%--------------------------------------------------------------------------
hrf = spm_hrf(dt);

% Create convolved explanatory {Hxb} variables in scan time
%--------------------------------------------------------------------------
xb  = spm_dctmtx(N*NT + 128,N);
Hxb = zeros(N,N);
for i = 1:N
    Hx       = conv(xb(:,i),hrf);
    Hxb(:,i) = Hx(k + 128);
end
xb = xb(129:end,:);

% Get confounds (in scan time) and constant term
%--------------------------------------------------------------------------
X0 = xY(1).X0;
M  = size(X0,2);

% Get response variable
%--------------------------------------------------------------------------
for i = 1:size(xY,2)
    Y(:,i) = xY(i).u;
end

% Remove confounds and save Y in ouput structure
%--------------------------------------------------------------------------
Yc    = Y - X0*inv(X0'*X0)*X0'*Y;
PPI.Y = Yc(:,1);
if size(Y,2) == 2
    PPI.P = Yc(:,2);
end

% Specify covariance components; assume neuronal response is white
% treating confounds as fixed effects
%--------------------------------------------------------------------------
Q = speye(N,N)*N/trace(Hxb'*Hxb);
Q = blkdiag(Q, speye(M,M)*1e6  );

% Get whitening matrix (NB: confounds have already been whitened)
%--------------------------------------------------------------------------
W = SPM.xX.W(Sess.row,Sess.row);

% Create structure for spm_PEB
%--------------------------------------------------------------------------
clear P
P{1}.X = [W*Hxb X0];        % Design matrix for lowest level
P{1}.C = speye(N,N)/4;      % i.i.d assumptions
P{2}.X = sparse(N + M,1);   % Design matrix for parameters (0's)
P{2}.C = Q;

switch ppiflag

    case  {'simple deconvolution','sd'}
    %======================================================================
    C  = spm_PEB(Y,P);
    xn = xb*C{2}.E(1:N);
    xn = spm_detrend(xn);

    % Save variables (NOTE: xn is in microtime and does not account for
    % slice timing shifts). To convert to BOLD signal convolve with a hrf.
    % Use a microtime to scan time index to convert to scan time: e.g.,
    % k = 1:NT:N*NT; where NT = number of bins per TR = TR/dt or SPM.xBF.T
    % and N = number of scans in the session. Finally account for slice
    % timing effects by shifting the index accordingly.
    %----------------------------------------------------------------------
    PPI.xn = xn;

    case  {'physiophysiologic interaction','phipi'}
    %======================================================================
    C    = spm_PEB(Y(:,1),P);
    xn1  = xb*C{2}.E(1:N);
    C    = spm_PEB(Y(:,2),P);
    xn2  = xb*C{2}.E(1:N);
    xn1  = spm_detrend(xn1);
    xn2  = spm_detrend(xn2);
    xnxn = xn1.*xn2;

    % Convolve, convert to scan time, and account for slice timing shift
    %----------------------------------------------------------------------
    ppi = conv(xnxn,hrf);
    ppi = ppi((k-1) + fMRI_T0);

    % Save variables
    %----------------------------------------------------------------------
    PPI.xn  = [xn1 xn2];
    PPI.ppi = spm_detrend(ppi);

    case  {'psychophysiologic interaction','ppi'}
    %======================================================================

    % COMPUTE PSYCHOPHYSIOLOGIC INTERACTIONS
    % use basis set in microtime
    %----------------------------------------------------------------------
    % get parameter estimates and neural signal; beta (C) is in scan time
    % This clever trick allows us to compute the betas in scan time which
    % is much quicker than with the large microtime vectors. Then the betas
    % are applied to a microtime basis set generating the correct neural
    % activity to convolve with the psychological variable in microtime
    %----------------------------------------------------------------------
    C  = spm_PEB(Y,P);
    xn = xb*C{2}.E(1:N);
    xn = spm_detrend(xn);

    % Setup psychological variable from inputs and contrast weights
    %----------------------------------------------------------------------
    PSY = zeros(N*NT,1);
    for i = 1:size(U.u,2)
        PSY = PSY + full(U.u(:,i) * U.w(i));
    end
    PSY = spm_detrend(PSY);

    % Multiply psychological variable by neural signal
    %----------------------------------------------------------------------
    PSYxn = PSY.*xn;

    % Convolve, convert to scan time, and account for slice timing shift
    %----------------------------------------------------------------------
    ppi = conv(PSYxn,hrf);
    ppi = ppi((k-1) + fMRI_T0);

    % Convolve psych effect, convert to scan time, and account for slice
    % timing shift
    %----------------------------------------------------------------------
    PSYHRF = conv(PSY,hrf);
    PSYHRF = PSYHRF((k-1) + fMRI_T0);

    % Save psychological variables
    %----------------------------------------------------------------------
    PPI.psy = U;
    PPI.P   = PSYHRF;
    PPI.xn  = xn;
    PPI.ppi = spm_detrend(ppi);

end % (switch)

% Display
%--------------------------------------------------------------------------
if showGraphics
    display_ppi(PPI);
end

% Save
%--------------------------------------------------------------------------
str    = ['PPI_' PPI.name '.mat'];

save(fullfile(SPM.swd,str),'PPI', spm_get_defaults('mat.format'))

cmd = 'spm_peb_ppi(''display'',''%s'')';
fprintf('   PPI saved as %s\n',spm_file(fullfile(SPM.swd,str),'link',cmd));

% Clean up
%--------------------------------------------------------------------------
set(Finter,'name',header);
cd(cwd);

%==========================================================================
% function display_ppi(PPI)
%==========================================================================
function display_ppi(PPI)

Fgraph = spm_figure('GetWin','PPI');
FS     = spm('FontSizes');
spm_clf(Fgraph);

try
    RT = PPI.RT;
catch
    RT = spm_get_defaults('stats.fmri.t')*PPI.dt; % backward compatibility
end
N  = length(PPI.xY(1).u);
NT = round(RT/PPI.dt);
t  = PPI.RT*[1:N];
T  = PPI.dt*[1:(N*NT)];

%-Simple deconvolution
%--------------------------------------------------------------------------
if numel(PPI.xY) == 1 && ~isfield(PPI,'psy')

    ax = subplot(1,1,1);
    plot(t,PPI.Y,T,PPI.xn)
    title('hemodynamic and neuronal responses')
    xlabel(['time (secs)'])
    axis tight square
    grid on
    legend('BOLD','neuronal')

    str = sprintf('Simple Deconvolution: %s\n',PPI.name);
    str = [str sprintf('VOI file: %s',PPI.xY.name)];

%-Physiophysiologic interaction
%--------------------------------------------------------------------------
elseif numel(PPI.xY) == 2

    ax = subplot(2,1,1);
    plot(t,PPI.ppi)
    title('PPI')
    xlabel(['time (secs)'])
    axis tight square
    grid on

    subplot(2,2,3)
    plot(t,PPI.Y(:,1),T,PPI.xn(:,1))
    title('hemodynamic and neuronal responses (1st)')
    xlabel(['time (secs)'])
    axis tight square
    grid on
    legend('BOLD','neuronal')

    subplot(2,2,4)
    plot(t,PPI.P,T,PPI.xn(:,2))
    title('hemodynamic and neuronal responses (2nd)')
    xlabel(['time (secs)'])
    axis tight square
    grid on
    legend('BOLD','neuronal')

    str = sprintf('Physiophysiologic Interaction: %s\n',PPI.name);
    str = [str, sprintf('VOI File 1: %s\n',PPI.xY(1).name)];
    str = [str, sprintf('VOI File 2: %s',PPI.xY(2).name)];

%-Psychophysiologic interaction
%--------------------------------------------------------------------------
elseif isfield(PPI,'psy')

    PSY = zeros(N*NT,1);
    for i = 1:size(PPI.psy.u,2)
        PSY = PSY + full(PPI.psy.u(:,i)*PPI.psy.w(:,i));
    end

    ax = subplot(2,1,1);
    plot(t,PPI.Y,T,PPI.xn(:,1))
    title('hemodynamic and neuronal responses')
    xlabel(['time (secs)'])
    axis tight square
    grid on
    legend('BOLD','neuronal')

    subplot(2,2,3)
    plot(T,PSY,'LineStyle','--','Color',[0 .65 0]);
    hold on
    plot(t,PPI.P,'LineStyle','-','LineWidth',1,'Color','b');
    hold off
    title('[convolved] psych. variable')
    xlabel(['time (secs)'])
    axis tight square
    grid on

    subplot(2,2,4)
    plot(t,PPI.ppi)
    title('PPI')
    xlabel(['time (secs)'])
    axis tight square
    grid on

    str = sprintf('Psychophysiologic Interaction: %s\n',PPI.name);
    str = [str, sprintf('VOI File: %s\n',PPI.xY(1).name)];
    str = [str, sprintf('Factors: ')];
    for i = 1:numel(PPI.psy.name)
        str = [str, sprintf('%s [%0.0f]',PPI.psy.name{i},PPI.psy.w(i))];
        if i < numel(PPI.psy.name)
            str = [str, sprintf('; ')];
        end
    end

else
    error('Unknown PPI type.');
end

hAx = axes('Position',[0 0.90 1 0.1],...
    'DefaultTextFontSize',FS(8),...
    'DefaultTextInterpreter','None',...
    'DefaultTextVerticalAlignment','Baseline',...
    'Parent',Fgraph,...
    'Units','points',...
    'Visible','off');
AxPos = get(hAx,'Position');
set(hAx,'XLim',[0,AxPos(3)]); set(hAx,'YLim',AxPos(2)+[0,AxPos(4)])
dy    = FS(9);
h     = text(dy,floor(AxPos(2)+AxPos(4))-2*dy,str);
set(h,'Parent',hAx);


%
% a script to show and sample default colors on the X windows system
% default colors are read from /usr/lib/X11/rgb.txt if it exists, else from myrgb.txt
% see also ROIcmap
%

Nmax = 2000;
name(Nmax).s = '';
name(Nmax).r = 0;
name(Nmax).g = 0;
name(Nmax).b = 0;
if (exist('/usr/lib/X11/rgb.txt') == 2),
   Colname = '/usr/lib/X11/rgb.txt';
   fprintf(1,'Using %s\n', Colname);
   ColFile = fopen (Colname,'r');
   %skip until first number
   tmp = fgets(ColFile);
   i = 0;
   while (tmp ~= -1 )
      tmp = zdeblank(tmp);
      if (afni_isdigit(tmp(1))),
         i = i + 1;
         [name(i).r,name(i).g,name(i).b,s1,s2,s3,s4] = strread(tmp,'%d%d%d %s %s %s %s');
         if (~isempty(s4)) name(i).s = sprintf('%s %s %s %s', char(s1), char(s2), char(s3), char(s4));
         elseif (~isempty(s3)) name(i).s = sprintf('%s %s %s', char(s1), char(s2), char(s3));
         elseif (~isempty(s2)) name(i).s = sprintf('%s %s', char(s1), char(s2));
         else name(i).s = char(s1);
         end
      end
      tmp = fgets(ColFile);
   end
   fclose (ColFile);
   N = i;
else
   ColFile = fopen ('myrgb.txt', 'r');
   Colname = 'myrgb.txt';
   fprintf(1,'Using %s\n', Colname);
   N = 752;
   for (i=1:1:N),
   name(i).r = fscanf(ColFile, '%g ', 1);
   name(i).g = fscanf(ColFile, '%g ', 1);
   name(i).b = fscanf(ColFile, '%g ', 1);
   name(i).s = fgets(ColFile);
   end
   fclose (ColFile);
end

name = name(1:N);

Mall = zeros (N, 3);
Mall(:,1) = [name(:).r]';
Mall(:,2) = [name(:).g]';
Mall(:,3) = [name(:).b]';
Mall = Mall./255;

if (0),
for (i=6:1:N-5),
   M = zeros (11,3);
   for (j=-5:1:5)
      M(j+6,1) = name(i+j).r./255;
      M(j+6,2) = name(i+j).g./255;
      M(j+6,3) = name(i+j).b./255;
   end
   colormap(M);
   subplot 211;
	image ([1:1:length(M(:,1))]);
   input ('Hit Enter:', 's');
end

end
figure(1), clf;
subplot (211);
str = sprintf('Colors in %s\nPick colors (background then foreground) with mouse\nHit "enter" to quit', Colname);
colormap(Mall); image ([1:1:N]); title (str, 'fontsize',14);
drawnow;
i = 0;
subplot (269);cla

x1 = 1;
while (~isempty(x1)),
   [x1,y] = ginput (1);
   if (~isempty(x1)),
      x1 = floor(x1(1)-0.5)+1;
      subplot (269);
      addsquare([0 i], [2.5 1+i], Mall(x1,:)); hold on
      plot (-0.2, i+0.5, 'k*');
      axis ([-1 3 -1 11]);
      str = sprintf ('back: %s', name(x1).s);
      title (str);
      [x2,y] = ginput (1);
      x2 = floor(x2(1)-0.5)+1;
      subplot (269);
      str = sprintf ('SUMA (%g %g)', x1, x2 );
      ht = text (0.3, 0.3+i, 0, str, 'fontsize',14, 'color', Mall(x2,:));
      str = sprintf ('back: %s (%g), fore: %s (%g)', name(x1).s, x1, name(x2).s, x2);
      title (str);
      i = rem(i +1, 10);
   end
end


classdef ExtremumValueConstraint < AbstractConstraint
    %ExtremumValueConstraint Summary of this class goes here
    %   Detailed explanation goes here

    properties
        normFact = 1;
        event LaunchVehicleEvent
        eventNode(1,1) ConstraintStateComparisonNodeEnum = ConstraintStateComparisonNodeEnum.FinalState;
        extremum LaunchVehicleExtrema

        lb(1,1) double = 0;
        ub(1,1) double = 0;

        evalType(1,1) ConstraintEvalTypeEnum = ConstraintEvalTypeEnum.FixedBounds;
        stateCompType(1,1) ConstraintStateComparisonTypeEnum = ConstraintStateComparisonTypeEnum.Equals;
        stateCompEvent LaunchVehicleEvent
        stateCompNode(1,1) ConstraintStateComparisonNodeEnum = ConstraintStateComparisonNodeEnum.FinalState;
    end

    methods
        function obj = ExtremumValueConstraint(event, lb, ub)
            obj.event = event;
            obj.lb = lb;
            obj.ub = ub;

            obj.id = rand();
        end

        function [lb, ub] = getBounds(obj)
            lb = obj.lb;
            ub = obj.ub;
        end

        function [c, ceq, value, lwrBnd, uprBnd, type, eventNum, valueStateComp] = evalConstraint(obj, stateLog, celBodyData)
            type = obj.getConstraintType();

            switch obj.eventNode
                case ConstraintStateComparisonNodeEnum.FinalState
                    stateLogEntry = stateLog.getLastStateLogForEvent(obj.event);

                case ConstraintStateComparisonNodeEnum.InitialState
                    stateLogEntry = stateLog.getFirstStateLogForEvent(obj.event);

                otherwise
                    error('Unknown event node.');
            end

            extremaStates = stateLogEntry.getAllExtremaStates();
            extremaState = extremaStates(extremaStates.extrema == obj.extremum);

            value = extremaState.value;

            if(obj.evalType == ConstraintEvalTypeEnum.StateComparison)
                switch obj.stateCompNode
                    case ConstraintStateComparisonNodeEnum.FinalState
                        stateLogEntryStateComp = stateLog.getLastStateLogForEvent(obj.stateCompEvent);

                    case ConstraintStateComparisonNodeEnum.InitialState
                        stateLogEntryStateComp = stateLog.getFirstStateLogForEvent(obj.stateCompEvent);

                    otherwise
                        error('Unknown event node.');
                end

                extremaStates = stateLogEntryStateComp.getAllExtremaStates();
                extremaState = extremaStates(extremaStates.extrema == obj.extremum);

                valueStateComp = extremaState.value;
            else
                valueStateComp = NaN;
            end

            [c, ceq] = obj.computeCAndCeqValues(value, valueStateComp);

            lwrBnd = obj.lb;
            uprBnd = obj.ub;

            eventNum = obj.event.getEventNum();
        end

        function sF = getScaleFactor(obj)
            sF = obj.normFact;
        end

        function setScaleFactor(obj, sF)
            obj.normFact = sF;
        end

        function tf = usesStage(obj, stage)
            tf = false;
        end

        function tf = usesEngine(obj, engine)
            tf = false;
        end

        function tf = usesTank(obj, tank)
            tf = false;
        end

        function tf = usesEngineToTankConn(obj, engineToTank)
            tf = false;
        end

        function tf = usesEvent(obj, event)
            tf = obj.event == event;
            if(obj.evalType == ConstraintEvalTypeEnum.StateComparison)
                tf = tf || obj.stateCompEvent == event;
            end
        end

        function tf = usesStopwatch(obj, stopwatch)
            tf = false;
        end

        function tf = usesExtremum(obj, extremum)
            tf = [obj.extremum] == extremum;
        end

        function tf = canUseSparseOutput(obj)
            tf = false;
        end

        function event = getConstraintEvent(obj)
            event = obj.event;
        end

        function type = getConstraintType(obj)
            type = 'Extremum Value';
        end

%         function name = getName(obj)
%             name = sprintf('%s - Event %i', obj.getConstraintType(), obj.event.getEventNum());
%         end

        function [unit, lbLim, ubLim, usesLbUb, usesCelBody, usesRefSc] = getConstraintStaticDetails(obj)
            unit = obj.extremum.unitStr;
            lbLim = -Inf;
            ubLim = Inf;
            usesLbUb = true;
            usesCelBody = false;
            usesRefSc = false;
        end

        function addConstraintTf = openEditConstraintUI(obj, lvdData)
%             [listBoxStr, extrema] = lvdData.launchVehicle.getExtremaListBoxStr();
%
%             if(isempty(extrema))
%                 addConstraintTf = false;
%
%                 warndlg('Cannot create extrema value object: no extrema have been created.  Create an extremum first.','Extremum Value Constraint','modal');
%             else
%                 [Selection,ok] = listdlg('PromptString',{'Select an extremum:'},...
%                                 'SelectionMode','single',...
%                                 'Name','Extremum',...
%                                 'ListString',listBoxStr);
%
%                 if(ok == 0)
%                     addConstraintTf = false;
%                 else
%                     ex = extrema(Selection);
%                     obj.extremum = ex;
%
%                     addConstraintTf = lvd_EditGenericMAConstraintGUI(obj, lvdData);
%                 end
%             end

            ex = obj.selectConstraintObj(lvdData);

            if(not(isempty(ex)))
                obj.extremum = ex;
%                 addConstraintTf = lvd_EditGenericMAConstraintGUI(obj, lvdData);

                output = AppDesignerGUIOutput({false});
                lvd_EditGenericMAConstraintGUI_App(obj, lvdData, output);
                addConstraintTf = output.output{1};
            else
                addConstraintTf = false;
            end
        end

        function ex = selectConstraintObj(obj, lvdData)
            [listBoxStr, extrema] = lvdData.launchVehicle.getExtremaListBoxStr();

            ex = [];
            if(isempty(extrema))
                warndlg('Cannot create extrema value object: no extrema have been created.  Create an extremum first.','Extremum Value Constraint','modal');
            else
                [Selection,ok] = listdlg('PromptString',{'Select an extremum:'},...
                                'SelectionMode','single',...
                                'Name','Extremum',...
                                'ListString',listBoxStr);

                if(ok == 0)
                    ex = [];
                else
                    ex = extrema(Selection);
                end
            end
        end

        function useObjFcn = setupForUseAsObjectiveFcn(obj,lvdData)
            ex = obj.selectConstraintObj(lvdData);

            if(not(isempty(ex)))
                obj.extremum = ex;
                useObjFcn = true;
            else
                useObjFcn = false;
            end
        end
    end

    methods(Static)
        function constraint = getDefaultConstraint(~, ~)
            constraint = ExtremumValueConstraint(LaunchVehicleEvent.empty(1,0),0,0);
        end
    end
end


function [imVol, mmPerVox, gradDirs, bVals, xformToCannonical] = dtiAverageRawData(firstDataFile, gradsFile)
% Intelligent averaging of diffusion-weighted images.
%
% [imVol, mmPerVox, gradDirs, bVals, xformToCannonical] = dtiAverageRawData(firstDataFile, gradsFile);
%
% Loads a whole directory of raw image files and combines them according to
% the gradient direction information in gradsFile. All repeated
% measurements are averaged. If coreg is true (default), it will also try
% to align each b>0 image to the b=0 image using a 12-parameter
% mutual-information algorithm (from SPM2). This should remove much of the
% eddy-current distortion.
%
% Also returns the xform that will rotate the images into our cannonical
% orientation (as close to axial as possible- see
% computeCannonicalXformFromIfile).
%
% TODO:
% * coreg should also do motion correction. To implement this, we need to
% do the coregistration step before we combine the repeated measurements.
% Of course, we should also correct the gradDirs for non-trivial motion.
%
%
% HISTORY:
% 2005.05.25: RFD (bob@sirl.stanford.edu) wrote it.
%

if(~exist('gradsFile','var')|isempty(gradsFile))
    % We could also try to infer the file based on info in the dicom
    % header.
    if(isunix)
      default = '/usr/local/dti/diffusion_grads/dwepi.13.grads';
    else
      default = pwd;
    end
    [f,p] = uigetfile({'*.grads';'*.*'},'Select dwepi grads file...',default);
    if(isequal(f,0)||isequal(p,0))
        error('User cancelled.');
    end
    gradsFile = fullfile(p,f);
end
if(~exist('firstDataFile','var')|isempty(firstDataFile))
    [f,p] = uigetfile({'*.dcm';'*.*'},'Select the first data file...');
    if(isequal(f,0)||isequal(p,0))
        error('User cancelled.');
    end
    firstDataFile = fullfile(p,f);
end

% Flag to turn on MI-based coregistration (eddy-current correction)
if(~exist('coreg','var')|isempty(coreg))
    coreg = false;
end

gradDirs = dlmread(gradsFile);
scannerToPhysXform = computeXformFromIfile(firstDataFile);
[xformToCannonical, baseName, mmPerVox, imDim, notes, sliceSkip] = computeCannonicalXformFromIfile(firstDataFile);
% *** TODO: may want to include im_hdr.scanspacing!
%mmPerVox(3) = mmPerVox(3)+sliceSkip;

% *** TODO: We assume that all non-zero b-vals are the same
hdr = dicominfo(firstDataFile);
bVals = hdr.Private_0019_10b0;

% HACK! TO deal with our interleaved data:
imDim(3) = imDim(3)*2;

[dataDir,f,ext] = fileparts(firstDataFile);
allFiles = dir(fullfile(dataDir, ['*' ext]));
nDirs = size(gradDirs,1);
nSlices = imDim(3);
filesPerRep = nDirs*nSlices;
nReps = length(allFiles)/filesPerRep;
if(nReps~=round(nReps))
    error('Non-integer number of repeats. Something is funky- refusing to go on...');
end

% TO DO:
% * motion correction/outlier rejection
%
disp('Loading all the data files...');
imVol = zeros([imDim nDirs]);
for(d=1:nDirs)
  for(n=1:nReps)
    fn = [[1:nSlices]+(d-1)*nSlices+((n-1)*filesPerRep)];
    for s = 1:nSlices;
      curName = sprintf('I%04d.dcm', fn(s));
      tmpImg = readRawImage(fullfile(dataDir,curName), 0, nPix(1:2), 'b');
      imVol(:,:,s,d) = imVol(:,:,s,d)+double(tmpImg);
    end
  end
  %tmpIm = makeCubeIfiles(dataDir, nPix(1:2), fileNums);
end
imVol = imVol./nReps;

disp('Merging duplicate directions...');
% Merge duplicates
[gDirs,inds] = unique(gradDirs,'rows');
% undo unique's sorting
gDirs = gradDirs(sort(inds),:);
nDirs = size(gDirs,1);
junk= [];
for(ii=1:nDirs)
  dups = gDirs(ii,1)==gradDirs(:,1)&gDirs(ii,2)==gradDirs(:,2)& ...
         gDirs(ii,3)==gradDirs(:,3);
  dups = find(dups);
  if(length(dups)>1)
    imVol(:,:,:,dups(1)) = mean(imVol(:,:,:,dups),4);
    junk = [junk dups(2:end)];
  end
end
imVol(:,:,:,junk) = [];

if(coreg)
  disp('Coregistering...');
  % NOTE: The following MI-based image registration tries to
  % coregister each direction map to the b0. It doen't seem to do
  % anything bad, but isn't particularly effective at removing the
  % eddy-current distortion. I guess it's not a 3d affine xform?
  % But, if we ran it on the individual images before averaging, it
  % would probably be a good way to remove motion.
  b0 = imVol(:,:,:,1);
  b0 = uint8(mrAnatHistogramClip(b0, 0.4, 0.99)*255+0.5);
  h = mrvWaitbar(0,'Performing eddy current correction...');
  for(d=2:nDirs)
    xf{d} = mrAnatRegister(imVol(:,:,:,d), b0);
    tmpIm = mrAnatResliceSpm(imVol(:,:,:,d),xf{d},[],[1 1 1],[7 7 7 0 0 0],0);
    tmpIm(isnan(tmpIm)) = 0;
    tmpIm(tmpIm<0) = 0;
    imVol(:,:,:,d) = tmpIm;
    mrvWaitbar(d/nDirs,h);
  end
  close(h);
end
imVol = int16(round(imVol));
return;

% Showing a movie (to see the image mis-alignment)
for(d=1:nDirs)
  f = squeeze(imVol(:,:,20,d));
  f = uint8(f./max(f(:)).*255+0.5);
  m(d) = im2frame(f,gray(256));
end
figure(99);
image(f); colormap(gray(256));
truesize; axis off
movie(m,1,5);


%% Define the objective function.
addpath(genpath(pwd()));
cd AC_blackbox_eval/blackbox_eval_source/
config_file = 'config_file.txt';
numRun = 1;
scenario_file = 'scenario-lpsolve-CORLAT-inst.txt';
[func, obj] = define_func_handle(config_file, scenario_file, numRun);
cd ../..

%% Optimize.
yaml = 'lpsolve.yaml';
model = highdim_opt(yaml, obj);


%% --------------------------
% DRRN_B1U9
% -------------------------------
function test_DRRN_B1U9()
setenv('LC_ALL','C')
addpath /data2/taiying/MSU_Code/my_caffe/matlab; % change to your caffe path
setenv('GLOG_minloglevel','2')
addpath('../');
addpath('../evaluation_func/');
addpath('../evaluation_func/matlabPyrTools-master/');

%% parameters
gpu_id = 2;
up_scale = 3;
data_set_id = 1;

iter_interval = 6536;
startNUM = 71;
endNUM = 71;
thresh = 1;
thresh_hei = 150; % threshold patch size for inference, since too big image may cost too much memory
thresh_wid = 150;
rf = 16;

pathfolder = ['../../data/'];
if data_set_id == 1
    % Set5
    setTestCur = 'Set5';
    path = [pathfolder setTestCur '/'];
    d = dir([path '*.bmp']);
    filenum = 5;
end
if data_set_id == 2
    % Set14
    setTestCur = 'Set14';
    path = [pathfolder setTestCur '/'];
    d = dir([path '*.bmp']);
    filenum = 14;
end
if data_set_id == 3
    % B100
    setTestCur = 'B100';
    path = [pathfolder setTestCur '/'];
    d = dir([path '*.jpg']);
    filenum = 100;
end
if data_set_id == 4
    % Urban100
    setTestCur = 'Urban100';
    path = [pathfolder setTestCur '/'];
    d = dir([path '*.png']);
    filenum = 100;
end

savepath = ['./results/'];
folderResultCur = fullfile(savepath, [setTestCur,'_x',num2str(up_scale)]);
%%% folder to store results
if ~exist(folderResultCur,'file')
    mkdir(folderResultCur);
end

mean_bicubic = [];
mean_drrn = [];
% caffe.set_mode_cpu(); % for CPU
caffe.set_mode_gpu(); % for GPU
caffe.set_device(gpu_id);

for NUM = startNUM:thresh:endNUM
    epochNUM = NUM*iter_interval;
    weights = ['../../model/DRRN_B1U9_20C128_iter_' num2str(epochNUM) '.caffemodel'];
    model_path = './DRRN_B1U9_20C128_deploy';

    bicubic_set =[];
    drrn_set = [];
    im_h_set = cell(filenum,1);
    im_gnd_set = cell(filenum,1);
    im_other_set = cell(6,1);
    scaleset = [];
    gatingset = [];

    for iii = 1:1:length(d)
        disp(['NUM: ' num2str(NUM) '  id: ' num2str(iii)]);
        imageName = d(iii).name;
        imageName = imageName(1:end-4);
        im  = imread([path d(iii).name]);

        %% work on luminance only
        im_ycbcr= im;
        if size(im,3)>1
            im_ycbcr = rgb2ycbcr(im);
            im_cb = im2double(im_ycbcr(:, :, 2));
            im_cr = im2double(im_ycbcr(:, :, 3));
            im_cb_gnd = modcrop(im_cb, up_scale);
            im_cr_gnd = modcrop(im_cr, up_scale);
            im_cb_b = imresize(imresize(im_cb_gnd,1/up_scale,'bicubic'),up_scale,'bicubic');
            im_cb_b = single(im_cb_b);
            im_cr_b = imresize(imresize(im_cr_gnd,1/up_scale,'bicubic'),up_scale,'bicubic');
            im_cr_b = single(im_cr_b);
        end
        im_y = im2double(im_ycbcr(:, :, 1));
        im_y_gnd = modcrop(im_y, up_scale);

        [hei,wid] = size(im_y_gnd);
        im_y_b = imresize(imresize(im_y_gnd,1/up_scale,'bicubic'),up_scale,'bicubic');
        im_y_b = single(im_y_b);

        %% adaptively spilt
        % decide patch numbers
        hei_patch = ceil(hei/(thresh_hei+rf));
        wid_patch = ceil(wid/(thresh_wid+rf));
        hei_stride = ceil(hei/hei_patch);
        wid_stride = ceil(wid/wid_patch);
        use_start_x = 0;
        use_start_y = 0;
        use_end_x = 0;
        use_end_y = 0;

        ext_start_x = 0;
        ext_end_x = 0;
        ext_start_y = 0;
        ext_end_y = 0;

        posext_start_x = 0;
        posext_start_y = 0;
        posext_end_x = 0;
        posext_end_y = 0;

        % extract each patch for inference
        im_y_h = [];
        temp1 = [];
        temp2 = [];
        temp3 = [];
        temp4 = [];
        temp5 = [];
        temp6 = [];
        for x = 1 : hei_stride : hei
            for y = 1 : wid_stride : wid
                % decide the length of hei and wid for each patch
                use_start_x = x;
                use_start_y = y;
                if x - rf > 1 % add border
                    ext_start_x = x-rf;
                    posext_start_x = rf+1;
                else
                    ext_start_x = x;
                    posext_start_x = 1;
                end
                if y-rf > 1
                    ext_start_y = y-rf;
                    posext_start_y = rf+1;
                else
                    ext_start_y = y;
                    posext_start_y = 1;
                end

                use_end_x = use_start_x+hei_stride-1;
                use_end_y = use_start_y+wid_stride-1;

                if use_start_x+hei_stride+rf-1 <= hei
                    hei_length = hei_stride+rf;
                    ext_end_x = use_start_x+hei_length-1;
                    posext_end_x = hei_length-rf+posext_start_x-1;

                else
                    hei_length = hei-ext_start_x+1;
                    ext_end_x = ext_start_x+hei_length-1;
                    posext_end_x = hei_length;
                    use_end_x = ext_start_x+hei_length-1;
                end
                if use_start_y+wid_stride+rf-1 <= wid
                    wid_length = wid_stride+rf;
                    ext_end_y = use_start_y+wid_length-1;
                    posext_end_y = wid_length-rf+posext_start_y-1;

                else
                    wid_length = wid-ext_start_y+1;
                    ext_end_y = ext_start_y+wid_length-1;
                    posext_end_y = wid_length;
                    use_end_y = ext_start_y+wid_length-1;
                end

                subim_input = im_y_b(ext_start_x : ext_end_x, ext_start_y : ext_end_y);  % input
                data = permute(subim_input,[2, 1, 3]);
                model = [model_path '.prototxt'];
                subim_output = do_cnn(model,weights,data);
                subim_output = subim_output';
                subim_output = subim_output(posext_start_x:posext_end_x,posext_start_y:posext_end_y);

                % fill im_h with sub_output
                im_y_h(use_start_x:use_end_x,use_start_y:use_end_y) = subim_output;

            end
        end

        %% remove border
        im_y_h1 = shave(uint8(single(im_y_h) * 255), [up_scale, up_scale]);
        im_y_gnd1 = shave(uint8(single(im_y_gnd) * 255), [up_scale, up_scale]);
        im_y_b1 = shave(uint8(single(im_y_b) * 255), [up_scale, up_scale]);

        if size(im,3) > 1
            im_cb_b1 = shave(uint8(single(im_cb_b) * 255), [up_scale, up_scale]);
            im_cr_b1 = shave(uint8(single(im_cr_b) * 255), [up_scale, up_scale]);
            ycbcr_h = cat(3,(im_y_h1),(im_cb_b1),(im_cr_b1));
            im_h1 = ycbcr2rgb(ycbcr_h);

            im_cb_gnd1 = shave(uint8(single(im_cb_gnd) * 255), [up_scale, up_scale]);
            im_cr_gnd1 = shave(uint8(single(im_cr_gnd) * 255), [up_scale, up_scale]);
            ycbcr_gnd = cat(3,(im_y_gnd1),(im_cb_gnd1),(im_cr_gnd1));
            im_gnd1 = ycbcr2rgb(ycbcr_gnd);
        else
            im_h1 = im_y_h1;
            im_gnd1 = im_y_gnd1;
        end
        im_h_set{iii} = im_h1;
        im_gnd_set{iii} = im_gnd1;

        %% save images
        imwrite(im_h1,fullfile(folderResultCur,[imageName,'_x',num2str(up_scale),'.png']));

        %% compute PSNR and SSIM and IFC
        bic(1) = compute_psnr(im_y_gnd1,im_y_b1);
        drrn(1) = compute_psnr(im_y_gnd1,im_y_h1);
        bic(2) = ssim_index(im_y_gnd1,im_y_b1);
        drrn(2) = ssim_index(im_y_gnd1,im_y_h1);
        bic(3) = ifcvec(double(im_y_gnd1),double(im_y_b1));
        drrn(3) = ifcvec(double(im_y_gnd1),double(im_y_h1));

        bicubic_set = [bicubic_set; bic];
        drrn_set = [drrn_set; drrn];
    end
    mean_bicubic = [mean_bicubic; [mean(bicubic_set(:,1)) mean(bicubic_set(:,2)) mean(bicubic_set(:,3))]];
    mean_drrn = [mean_drrn; [mean(drrn_set(:,1)) mean(drrn_set(:,2)) mean(drrn_set(:,3))]];
end

%%% save PSNR and SSIM metrics
PSNR_set = drrn_set(:,1);
SSIM_set = drrn_set(:,2);
IFC_set = drrn_set(:,3);
save(fullfile(folderResultCur,['PSNR_',setTestCur,'_x',num2str(up_scale),'.mat']),['PSNR_set'])
save(fullfile(folderResultCur,['SSIM_',setTestCur,'_x',num2str(up_scale),'.mat']),['SSIM_set'])
save(fullfile(folderResultCur,['IFC_',setTestCur,'_x',num2str(up_scale),'.mat']),['IFC_set'])

count = 1;
for kk = startNUM:thresh:endNUM
    disp(['epoch: ' num2str(kk) '---- bic = ' num2str(mean_bicubic(count,:)) '---- DRRN = ' num2str(mean_drrn(count,:))]);
    count = count+1;
end

end


function [dim lf] = readBESAlft(filename, chanid)

% readBESAlft reads the leadfield from the BESA leadfield file format.
% The leadfield matrix and a vector with the leadfield dimensions are
% returned.
%
% Parameters:
%     [filename]
%         In the case that the current folder is not the folder containing
%         the file it should be the full path including the name of the
%         lft file else only the name of the file should be specified.
%
%     [chanid]
%         Additional parameter specifying particular channel for which
%         leadfield information should be returned.
%
% Return:
%     [dim]
%         Leadfield dimensions {<number sensors>, <number source space
%         nodes>, <number source directions>}. Please note, in case that
%         the additional parameter is specified, the number of sensors will
%         be set to 1.
%
%     [lf]
%         Leadfield matrix with <number sensors> rows and <number source
%         space nodes> x <number source directions> columns. Each row
%         contains first the potentials for sources at all nodes in x-dir,
%         then for sources at all nodes in y-dir, and finally for all
%         nodes in z-dir.
%
% Copyright (C) 2013, BESA GmbH
%
% File name: readBESAlft.m
%
% Author: Benjamin Lanfer/Robert Spangler
% Created: 2013-11-27

% Check additional parameter
if exist('chanid', 'var')
    % check if chanid is scalar
    if isscalar(chanid)
        % get leadfield info from specific channels
    else
        % error; only scalar values allowed for chanid
        error('Parameter chanid is not a scalar value!');
    end;
else
    % get leadfield info all channels
    chanid = [];
end;

% Try to open file.
FileID = fopen(filename, 'rb');
if(FileID < 0)
	fprintf('Error opening file.\n');
	return
end

% Read version number (int32)
[VersionNumber NrReadElements] = fread(FileID, 1, 'int32');
if(NrReadElements ~= 1)
	fprintf('Could not read number of elements.\n');
	return
end

% Check version number
ExpectedVersionNumber = 1;
if(VersionNumber ~= ExpectedVersionNumber)
	fprintf('Wrong version number. Expected: %d, read %d.\n', ExpectedVersionNumber, ...
		VersionNumber);
	return
end

% Read number of sensors (int32)
[NumberSensors NrReadElements] = fread(FileID, 1, 'int32');
if(NrReadElements ~= 1)
	fprintf('Could not read number of sensors.\n');
	return
end

% In case the leadfield for one specific channel has to be returned, check
% if chanid is in correct range.
if ~isempty(chanid)
    if ((chanid > NumberSensors) ||...
        (chanid < 1))
        % error; chanid out of range
        error('Parameter chanid is out of range!');
    end;
end;

% Read number of source space nodes (int32)
[NumberSourceSpaceNodes NrReadElements] = fread(FileID, 1, 'int32');
if(NrReadElements ~= 1)
	fprintf('Could not read number of source space nodes.\n');
	return
end

% Read number of source directions per node (int32)
[NumberDirections NrReadElements] = fread(FileID, 1, 'int32');
if(NrReadElements ~= 1)
	fprintf('Could not read number of source directions.\n');
	return
end

% Read maximum LF values for each source (source nodes x directions) (float32)
NumberColumns = NumberDirections * NumberSourceSpaceNodes;
[MaxVals NrReadElements] = fread(FileID, NumberColumns, 'float32');
if(NrReadElements ~= NumberColumns)
	fprintf('Could not read maximum leadfield values from file.\n');
	return
end

if ~isempty(chanid)
    % Get leadfield for specific channel only
    lf = [];
    NumberSensors = 1;

    % Go to correct position in file where leadfield info for current
    % channel is stored
    NumSkippedBytes = (chanid-1)*NumberSourceSpaceNodes*NumberDirections*2;
    status = fseek(FileID, NumSkippedBytes, 0);
    if status ~= 0
        error('Unable to move to specified position in file.');
    end;

    % Read compactly stored LF values for channel #chanid (int16)
    NumberLFValues = NumberColumns * 1;
    [CompactLFVals NrReadElements] = fread(FileID, NumberLFValues, 'int16');
    if(NrReadElements ~= NumberLFValues)
        fprintf('Could not read leadfield values from file.\n');
        return
    end

else
    % Get leadfield for all channels

    % Read compactly stored LF values (int16)
    NumberLFValues = NumberColumns * NumberSensors;
    [CompactLFVals NrReadElements] = fread(FileID, NumberLFValues, 'int16');
    if(NrReadElements ~= NumberLFValues)
        fprintf('Could not read leadfield values from file.\n');
        return
    end
end

% Reshape matrix with compact LF values.
CompactLFVals = reshape(CompactLFVals, NumberColumns, NumberSensors);
CompactLFVals = CompactLFVals';

% Compute factors for converting compactly stored LF values
% to float values.
ConversionFactors = MaxVals / 32767.0;

% Clear superfluous parameters
clear('MaxVals');

% Convert compact LF values to float.
%lf = CompactLFVals * repmat(ConversionFactors, 1, NumberColumns);

% NOTE RS:
% * operator in line %lf = CompactLFVals * repmat(...) is a real
% matrix multiplication, not a scalar multiplication. Is this intended?

% NOTE RS:
% Use alternative conversion of compact LF values to float LF
% values due to the extrem memory consumption of the repmat function:
for iCol=1:NumberColumns
    CompactLFVals(:,iCol) = CompactLFVals(:,iCol)*ConversionFactors(iCol);
end
lf = CompactLFVals;

% Copy dimensions.
dim = [NumberSensors; NumberSourceSpaceNodes; NumberDirections];

% Close file.
fclose(FileID);
end


function [m, bl] = model_add_block(m, varargin);
% Add a block of parameters to the model.
%   [m, bl] = model_add_block(m, varargin)
%
% Return values
%   m         Updated model
%
% Arguments
%   m         Model to update
%   varargin  (key, value) pairs that can specify the following:
%   key             value
%   ---             -----
%   reg_mult        Block regularization cost
%   learn           0 => parameters are not learned; 1 => they are learned
%   lower_bounds    Lower-bound box contraints for each parameter in this block
%   shape           Dimensions of this block (e.g., [6 6 32])
%   w               Block parameter values
%   type            Block type from the block_type enumeration

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

valid_opts = {'reg_mult', 'learn', 'lower_bounds', 'shape', 'w', 'type'};
opts = getopts(varargin, valid_opts);

% OPT: w
if opts.isKey('w')
  w = opts('w');
else
  w = [];
end

% OPT: shape
if opts.isKey('shape')
  shape = opts('shape');
  if isempty(w)
    w = zeros(prod(shape), 1);
  end
else
  shape = size(w);
end

% Dimension of the block's parameter vector
dim = prod(shape);

% OPT: reg_mult
if opts.isKey('reg_mult')
  reg_mult = opts('reg_mult');
else
  reg_mult = 1;
end

% OPT: learn
if opts.isKey('learn')
  learn = opts('learn');
else
  learn = 1;
end

% OPT: lower_bounds
if opts.isKey('lower_bounds')
  lower_bounds = opts('lower_bounds');
else
  lower_bounds = -inf*ones(dim, 1);
end

% OPT: type
if opts.isKey('type')
  btype = opts('type');
else
  btype = block_types.Other;
end

% Sanity checks
assert(~isempty(w));
assert(numel(w) == dim);
assert(size(lower_bounds,1) == dim);

% Allocate new block
bl = m.numblocks + 1;
m.numblocks = bl;

% Install block
m.blocks(bl).w        = w(:);
m.blocks(bl).lb       = lower_bounds;
m.blocks(bl).learn    = learn;
m.blocks(bl).reg_mult = reg_mult;
m.blocks(bl).dim      = dim;
m.blocks(bl).shape    = shape;
m.blocks(bl).type     = btype;