% --------- DeepMIMO: A Generic Dataset for mmWave and massive MIMO ------%
% Authors: Umut Demirhan, Abdelrahman Taha, Ahmed Alkhateeb
% Date: March 17, 2022
% Goal: Encouraging research on ML/DL for MIMO applications and
% providing a benchmarking tool for the developed algorithms
% ---------------------------------------------------------------------- %

function [channel_params,channel_params_BS,BS_loc]=read_raytracing(BS_ID, params, scenario_files)
%% Loading channel parameters between current active basesation transmitter and user receivers
filename_DoD=strcat(scenario_files, '.', int2str(BS_ID),'.DoD.mat');
filename_DoA=strcat(scenario_files, '.', int2str(BS_ID),'.DoA.mat');
filename_CIR=strcat(scenario_files, '.', int2str(BS_ID),'.CIR.mat');
filename_LOS=strcat(scenario_files, '.', int2str(BS_ID),'.LoS.mat');
filename_PL=strcat(scenario_files, '.', int2str(BS_ID),'.PL.mat');
filename_Loc=strcat(scenario_files, '.Loc.mat');

DoD_array=importdata(filename_DoD);
DoA_array=importdata(filename_DoA);
CIR_array=importdata(filename_CIR);
LOS_array=importdata(filename_LOS);
PL_array=importdata(filename_PL);
Loc_array=importdata(filename_Loc);

user_ids = double(params.user_ids);
user_last = double(max(user_ids));
num_paths = double(params.num_paths);
tx_power_raytracing = double(params.transmit_power_raytracing); %The transmit power in dBm used to generate the channel data
transmit_power = 30;

total_num_users=double(DoD_array(1));
pointer=0;

DoD_array(1)=[];
DoA_array(1)=[];
CIR_array(1)=[];
LOS_array(1)=[];

channel_params_all=struct('DoD_phi',[],'DoD_theta',[],'DoA_phi',[],'DoA_theta',[],'phase',[],'ToA',[],'power',[],'num_paths',[],'loc',[],'LoS_status',[]);
channel_params_all_BS =struct('DoD_phi',[],'DoD_theta',[],'DoA_phi',[],'DoA_theta',[],'phase',[],'ToA',[],'power',[],'num_paths',[],'loc',[],'LoS_status',[]);

dc = duration_check(params.symbol_duration);

user_count = 1;
[~,user_order] = sort(user_ids);
for Receiver_Number=1:user_last
    max_paths=double(DoD_array(pointer+2));
    num_path_limited=double(min(num_paths,max_paths));
    if ismember(Receiver_Number, user_ids)
        if (max_paths>0)
            Relevant_data_length=max_paths*4;
            Relevant_limited_data_length=num_path_limited*4;

            Relevant_DoD_array=DoD_array(pointer+3:pointer+2+Relevant_data_length);
            Relevant_DoA_array=DoA_array(pointer+3:pointer+2+Relevant_data_length);
            Relevant_CIR_array=CIR_array(pointer+3:pointer+2+Relevant_data_length);

            channel_params_all(user_order(user_count)).DoD_phi=Relevant_DoD_array(2:4:Relevant_limited_data_length);
            channel_params_all(user_order(user_count)).DoD_theta=Relevant_DoD_array(3:4:Relevant_limited_data_length);
            channel_params_all(user_order(user_count)).DoA_phi=Relevant_DoA_array(2:4:Relevant_limited_data_length);
            channel_params_all(user_order(user_count)).DoA_theta=Relevant_DoA_array(3:4:Relevant_limited_data_length);
            channel_params_all(user_order(user_count)).phase=Relevant_CIR_array(2:4:Relevant_limited_data_length);
            channel_params_all(user_order(user_count)).ToA=Relevant_CIR_array(3:4:Relevant_limited_data_length);
            channel_params_all(user_order(user_count)).power=1e-3*(10.^(0.1*( Relevant_CIR_array(4:4:Relevant_limited_data_length)+(transmit_power-tx_power_raytracing) )));
            channel_params_all(user_order(user_count)).num_paths=num_path_limited;
            channel_params_all(user_order(user_count)).loc=Loc_array(Receiver_Number,2:4);
            channel_params_all(user_order(user_count)).distance=PL_array(Receiver_Number,1);
            channel_params_all(user_order(user_count)).pathloss=PL_array(Receiver_Number,2);
            channel_params_all(user_order(user_count)).LoS_status=LOS_array(Receiver_Number);

            dc.add_ToA(channel_params_all(user_order(user_count)).power, channel_params_all(user_order(user_count)).ToA);

        else
            channel_params_all(user_order(user_count)).DoD_phi=[];
            channel_params_all(user_order(user_count)).DoD_theta=[];
            channel_params_all(user_order(user_count)).DoA_phi=[];
            channel_params_all(user_order(user_count)).DoA_theta=[];
            channel_params_all(user_order(user_count)).phase=[];
            channel_params_all(user_order(user_count)).ToA=[];
            channel_params_all(user_order(user_count)).power=[];
            channel_params_all(user_order(user_count)).num_paths=0;
            channel_params_all(user_order(user_count)).loc=Loc_array(Receiver_Number,2:4);
            channel_params_all(user_order(user_count)).distance=PL_array(Receiver_Number,1);
            channel_params_all(user_order(user_count)).pathloss=[];
            channel_params_all(user_order(user_count)).LoS_status=LOS_array(Receiver_Number);
        end
        user_count = double(user_count + 1);
    end
    pointer=double(pointer+max_paths*4+2);
end

dc.print_warnings('user', BS_ID);
dc.reset();

channel_params=channel_params_all(1,:);

%% Loading channel parameters between current active basesation transmitter and all the basestation receivers
if params.enable_BS2BSchannels
    filename_BSBS_DoD=strcat(scenario_files, '.', int2str(BS_ID),'.DoD.BSBS.mat');
    filename_BSBS_DoA=strcat(scenario_files, '.', int2str(BS_ID),'.DoA.BSBS.mat');
    filename_BSBS_CIR=strcat(scenario_files, '.', int2str(BS_ID),'.CIR.BSBS.mat');
    filename_BSBS_LOS=strcat(scenario_files, '.', int2str(BS_ID),'.LoS.BSBS.mat');
    filename_BSBS_PL=strcat(scenario_files, '.', int2str(BS_ID),'.PL.BSBS.mat');
    filename_BSBS_Loc=strcat(scenario_files, '.BSBS.RX_Loc.mat');

    DoD_BSBS_array=importdata(filename_BSBS_DoD);
    DoA_BSBS_array=importdata(filename_BSBS_DoA);
    CIR_BSBS_array=importdata(filename_BSBS_CIR);
    LOS_BSBS_array=importdata(filename_BSBS_LOS);
    PL_BSBS_array=importdata(filename_BSBS_PL);
    Loc_BSBS_array=importdata(filename_BSBS_Loc);

    num_paths_BSBS = double(params.num_paths);
    total_num_BSs=double(DoD_BSBS_array(1));
    BS_pointer=0;

    DoD_BSBS_array(1)=[];
    DoA_BSBS_array(1)=[];
    CIR_BSBS_array(1)=[];
    LOS_BSBS_array(1)=[];

    BS_count = 1;
    for Receiver_BS_Number=1:total_num_BSs
        max_paths_BSBS=double(DoD_BSBS_array(BS_pointer+2));
        num_path_BS_limited=double(min(num_paths_BSBS,max_paths_BSBS));
        if sum(Receiver_BS_Number == double(params.active_BS)) == 1 %Only read the channels related to the active basestation receivers

            if (max_paths_BSBS>0)
                Relevant_data_length_BSBS=max_paths_BSBS*4;
                Relevant_limited_data_length_BSBS=num_path_BS_limited*4;

                Relevant_DoD_BSBS_array=DoD_BSBS_array(BS_pointer+3:BS_pointer+2+Relevant_data_length_BSBS);
                Relevant_DoA_BSBS_array=DoA_BSBS_array(BS_pointer+3:BS_pointer+2+Relevant_data_length_BSBS);
                Relevant_CIR_BSBS_array=CIR_BSBS_array(BS_pointer+3:BS_pointer+2+Relevant_data_length_BSBS);

                channel_params_all_BS(BS_count).DoD_phi=Relevant_DoD_BSBS_array(2:4:Relevant_limited_data_length_BSBS);
                channel_params_all_BS(BS_count).DoD_theta=Relevant_DoD_BSBS_array(3:4:Relevant_limited_data_length_BSBS);
                channel_params_all_BS(BS_count).DoA_phi=Relevant_DoA_BSBS_array(2:4:Relevant_limited_data_length_BSBS);
                channel_params_all_BS(BS_count).DoA_theta=Relevant_DoA_BSBS_array(3:4:Relevant_limited_data_length_BSBS);
                channel_params_all_BS(BS_count).phase=Relevant_CIR_BSBS_array(2:4:Relevant_limited_data_length_BSBS);
                channel_params_all_BS(BS_count).ToA=Relevant_CIR_BSBS_array(3:4:Relevant_limited_data_length_BSBS);
                channel_params_all_BS(BS_count).power=1e-3*(10.^(0.1*( Relevant_CIR_BSBS_array(4:4:Relevant_limited_data_length_BSBS)+(transmit_power-tx_power_raytracing) )));
                channel_params_all_BS(BS_count).num_paths=num_path_BS_limited;
                channel_params_all_BS(BS_count).loc=Loc_BSBS_array(Receiver_BS_Number,2:4);
                channel_params_all_BS(BS_count).distance=PL_BSBS_array(Receiver_BS_Number,1);
                channel_params_all_BS(BS_count).pathloss=PL_BSBS_array(Receiver_BS_Number,2);
                channel_params_all_BS(BS_count).LoS_status=LOS_BSBS_array(Receiver_BS_Number);

                dc.add_ToA(channel_params_all_BS(BS_count).power, channel_params_all_BS(BS_count).ToA);

            else
                channel_params_all_BS(BS_count).DoD_phi=[];
                channel_params_all_BS(BS_count).DoD_theta=[];
                channel_params_all_BS(BS_count).DoA_phi=[];
                channel_params_all_BS(BS_count).DoA_theta=[];
                channel_params_all_BS(BS_count).phase=[];
                channel_params_all_BS(BS_count).ToA=[];
                channel_params_all_BS(BS_count).power=[];
                channel_params_all_BS(BS_count).num_paths=0;
                channel_params_all_BS(BS_count).loc=Loc_BSBS_array(Receiver_BS_Number,2:4);
                channel_params_all_BS(BS_count).distance=PL_BSBS_array(Receiver_BS_Number,1);
                channel_params_all_BS(BS_count).pathloss=[];
                channel_params_all_BS(BS_count).LoS_status=LOS_BSBS_array(Receiver_BS_Number);
            end
            BS_count = double(BS_count + 1);
        end
        BS_pointer=double(BS_pointer+max_paths_BSBS*4+2);
    end

    dc.print_warnings('BS', BS_ID);
    dc.reset()

end
channel_params_BS=channel_params_all_BS(1,:);

%% Loading current active basestation location
TX_Loc_array=importdata(strcat(scenario_files, '.TX_Loc.mat')); %Reading transmitter locations
BS_loc = TX_Loc_array(BS_ID,2:4); %Select current active basestation location

end


function [pred_boxes, scores, box_deltas_, anchors_, scores_] = proposal_im_detect(conf, caffe_net, im)
% [pred_boxes, scores, box_deltas_, anchors_, scores_] = proposal_im_detect(conf, im, net_idx)
% --------------------------------------------------------
% Faster R-CNN
% Copyright (c) 2015, Shaoqing Ren
% Licensed under The MIT License [see LICENSE for details]
% --------------------------------------------------------

    im = single(im);
    [im_blob, im_scales] = get_image_blob(conf, im);
    im_size = size(im);
    scaled_im_size = round(im_size * im_scales);

    % permute data into caffe c++ memory, thus [num, channels, height, width]
    im_blob = im_blob(:, :, [3, 2, 1], :); % from rgb to brg
    im_blob = permute(im_blob, [2, 1, 3, 4]);
    im_blob = single(im_blob);

    net_inputs = {im_blob};

    % Reshape net's input blobs
    caffe_net.reshape_as_input(net_inputs);
    output_blobs = caffe_net.forward(net_inputs);

    % Apply bounding-box regression deltas
    box_deltas = output_blobs{1};
    featuremap_size = [size(box_deltas, 2), size(box_deltas, 1)];
    % permute from [width, height, channel] to [channel, height, width], where channel is the
        % fastest dimension
    box_deltas = permute(box_deltas, [3, 2, 1]);
    box_deltas = reshape(box_deltas, 4, [])';

    anchors = proposal_locate_anchors(conf, size(im), conf.test_scales, featuremap_size);
    pred_boxes = fast_rcnn_bbox_transform_inv(anchors, box_deltas);
      % scale back
    pred_boxes = bsxfun(@times, pred_boxes - 1, ...
        ([im_size(2), im_size(1), im_size(2), im_size(1)] - 1) ./ ([scaled_im_size(2), scaled_im_size(1), scaled_im_size(2), scaled_im_size(1)] - 1)) + 1;
    pred_boxes = clip_boxes(pred_boxes, size(im, 2), size(im, 1));

    assert(conf.test_binary == false);
    % use softmax estimated probabilities
    scores = output_blobs{2}(:, :, end);
    scores = reshape(scores, size(output_blobs{1}, 1), size(output_blobs{1}, 2), []);
    % permute from [width, height, channel] to [channel, height, width], where channel is the
        % fastest dimension
    scores = permute(scores, [3, 2, 1]);
    scores = scores(:);

    box_deltas_ = box_deltas;
    anchors_ = anchors;
    scores_ = scores;

    if conf.test_drop_boxes_runoff_image
        contained_in_image = is_contain_in_image(anchors, round(size(im) * im_scales));
        pred_boxes = pred_boxes(contained_in_image, :);
        scores = scores(contained_in_image, :);
    end

    % drop too small boxes
    [pred_boxes, scores] = filter_boxes(conf.test_min_box_size, pred_boxes, scores);

    % sort
    [scores, scores_ind] = sort(scores, 'descend');
    pred_boxes = pred_boxes(scores_ind, :);
end

function [data_blob, rois_blob, im_scale_factors] = get_blobs(conf, im, rois)
    [data_blob, im_scale_factors] = get_image_blob(conf, im);
    rois_blob = get_rois_blob(conf, rois, im_scale_factors);
end

function [blob, im_scales] = get_image_blob(conf, im)
    if length(conf.test_scales) == 1
        [blob, im_scales] = prep_im_for_blob(im, conf.image_means, conf.test_scales, conf.test_max_size);
    else
        [ims, im_scales] = arrayfun(@(x) prep_im_for_blob(im, conf.image_means, x, conf.test_max_size), conf.test_scales, 'UniformOutput', false);
        im_scales = cell2mat(im_scales);
        blob = im_list_to_blob(ims);
    end
end

function [rois_blob] = get_rois_blob(conf, im_rois, im_scale_factors)
    [feat_rois, levels] = map_im_rois_to_feat_rois(conf, im_rois, im_scale_factors);
    rois_blob = single([levels, feat_rois]);
end

function [feat_rois, levels] = map_im_rois_to_feat_rois(conf, im_rois, scales)
    im_rois = single(im_rois);

    if length(scales) > 1
        widths = im_rois(:, 3) - im_rois(:, 1) + 1;
        heights = im_rois(:, 4) - im_rois(:, 2) + 1;

        areas = widths .* heights;
        scaled_areas = bsxfun(@times, areas(:), scales(:)'.^2);
        levels = max(abs(scaled_areas - 224.^2), 2);
    else
        levels = ones(size(im_rois, 1), 1);
    end

    feat_rois = round(bsxfun(@times, im_rois-1, scales(levels)) / conf.feat_stride) + 1;
end

function [boxes, scores] = filter_boxes(min_box_size, boxes, scores)
    widths = boxes(:, 3) - boxes(:, 1) + 1;
    heights = boxes(:, 4) - boxes(:, 2) + 1;

    valid_ind = widths >= min_box_size & heights >= min_box_size;
    boxes = boxes(valid_ind, :);
    scores = scores(valid_ind, :);
end

function boxes = clip_boxes(boxes, im_width, im_height)
    % x1 >= 1 & <= im_width
    boxes(:, 1:4:end) = max(min(boxes(:, 1:4:end), im_width), 1);
    % y1 >= 1 & <= im_height
    boxes(:, 2:4:end) = max(min(boxes(:, 2:4:end), im_height), 1);
    % x2 >= 1 & <= im_width
    boxes(:, 3:4:end) = max(min(boxes(:, 3:4:end), im_width), 1);
    % y2 >= 1 & <= im_height
    boxes(:, 4:4:end) = max(min(boxes(:, 4:4:end), im_height), 1);
end

function contained = is_contain_in_image(boxes, im_size)
    contained = boxes >= 1 & bsxfun(@le, boxes, [im_size(2), im_size(1), im_size(2), im_size(1)]);

    contained = all(contained, 2);
end


function patch_handle_array = draw_objsrc(obj_array, src_array, grid3d, withinterp, withpml)

chkarg(istypesizeof(obj_array, 'EMObject', [1 0]), ...
	'"obj_array" should be row vector with EMObject as elements.');
chkarg(istypesizeof(grid3d, 'Grid3d'), '"grid3d" should be instance of Grid3d.');
chkarg(istypesizeof(withinterp, 'logical'), '"withinterp" should be logical.');
chkarg(istypesizeof(withpml, 'logical'), '"withpml" should be logical.');

if withinterp
	lplot = grid3d.lplot(GT.prim, withinterp, withpml);
else
	lplot = grid3d.lvoxelbound(GT.prim, withpml);
end

for w = Axis.elems
	if length(lplot{w}) <= 3
		lplot{w} = linspace(lplot{w}(1), lplot{w}(end), 10);
	end
end

boxes_pml = EMObject.empty(0,0);
gray = [0.5 0.5 0.5];
if withpml
	for w = Axis.elems
		for s = Sign.elems
			if grid3d.Npml(w,s) > 0
				bound = grid3d.bound;
				bound(w, alter(s)) = grid3d.lpml(w,s);
				box = Box(bound);
				pml = Material('PML', gray, 1.0);
				box_pml = EMObject(box, pml);
				boxes_pml = [boxes_pml(1:end), box_pml];
			end
		end
	end
end

srcobj_array = EMObject.empty(0, length(src_array));
i = 0;
for src = src_array
	i = i+1;
	green = 'g';  % green
	srcmat = Material('Source', green, 1.0);
	srcobj_array(i) = EMObject(src.shape, srcmat);
end

patch_handle_array = [];
lplotobj = cell(1, Axis.count);  % indices
for obj = [obj_array, srcobj_array, boxes_pml]
	shape = obj.shape;
	color = obj.material.color;
% 	if ~isequal(color, 'none') && ~istypesizeof(shape, 'Domain')
	if ~isequal(color, 'none')
		for w = Axis.elems
			bn = shape.bound(w, Sign.n);
			bp = shape.bound(w, Sign.p);
			in = find(lplot{w} >= bn, 1, 'first');
			ip = find(lplot{w} <= bp, 1, 'last');

			% Find the indices for a box that is slighly larger than "shape".
			if in > 1
				in = in - 1;
			end
			if ip < length(lplot{w})
				ip = ip + 1;
			end
			lw = lplot{w}(in:ip);
			Nmin = 20;  % minimun number of sampling points
			if ~isempty(lw) && length(lw) < Nmin
				lw = linspace(lw(1), lw(end), Nmin);
			end
			lplotobj{w} = lw;
		end

		if ~isempty(lplotobj{Axis.x}) && ~isempty(lplotobj{Axis.y}) && ~isempty(lplotobj{Axis.z})  % shape in inside domain
			if ~istypesizeof(shape, 'ZeroVolShape')
				lsf = shape.lsf;
			else
				lsf = @(x,y,z) shape.lsf(x, y, z, true);
			end
			[X, Y, Z] = meshgrid(lplotobj{:});
			Level = lsf(X, Y, Z);
			hp = patch(isosurface(X, Y, Z, Level, -eps));  % -eps instead of 0 for ZeroVolumeShape
			isonormals(X, Y, Z, Level, hp)

			if istypesizeof(shape, 'Point')
				set(hp, 'FaceColor', color, 'EdgeColor', color, 'LineWidth', 3);
			elseif istypesizeof(shape, 'Point') || istypesizeof(shape, 'Plane')
				set(hp, 'FaceColor', color, 'EdgeColor', color, 'LineWidth', 1);
			else
				set(hp, 'FaceColor', color, 'EdgeColor', 'none');
			end
			if isequal(obj.material.name, 'PML') || isequal(obj.material.name, 'Source')
				alpha(hp, 0.2);
			end

			patch_handle_array = [patch_handle_array(1:end), hp];
		end
	end
end


function diagnostic = bisection_core(Constraints,Objective,options)
%BISECTION Core engine

% Note, outer layer added to enable arbitrary sign on ojective.
% Code here initially assumed maximization of scalar, so now we tweak the
% code a bit with swtchedsign to run any case. Fugly.

diagnostic.yalmiptime = 0;
diagnostic.solvertime = 0;
diagnostic.info = '';
diagnostic.problem = 0;

the_sign = 1;
if options.bisection.switchedsign
    the_sign = -1;
end

if length(getvariables(Objective)) > 1
    diagnostic.problem = -4;
    return
end

if  ~(isequal(getbase(Objective),[0 1]))
    diagnostic.problem = -4;
    return
end

if nargin < 3 || isempty(options)
    options = sdpsettings;
end

% Initialize the lower bound
% Typically a good guess
lower = 0;

% Silly upper bound
bestUpper = inf;

% Create an optimizer object which solves feasibility problem for a
% particular value of the sought variable
x = recover(setdiff(depends(Constraints),depends(Objective)));
if isempty(options) || isequal(options.solver,'')
    % We must figure out suitable solver
    [~,~,~,model] = export(replace(Constraints,Objective,pi));
    options.solver = model.solver.tag;
end
P = optimizer(Constraints,[],options,Objective,x);

% Safe-guard aganst using LMILAB
if ~isempty(strfind(struct(P).model.options.solver,'lmilab'))
    disp('Selected solver LMILAB lacks features making it unsuitable for BISECTION')
    disp('See why here https://yalmip.github.io/solver/lmilab/');
    disp('Select another SDP solver or/and install a better SDP solver');
    error('Cannot proceed due to poor SDP solver');
    return
end

if options.verbose;
    disp(['Selected solver: ' options.solver]);
    fprintf(['Generating initial bound: ' num2str(lower*the_sign)]);
end

% Make sure we actually can solve the lower problem
solvertime = tic;
[sol, flag] = P{lower};
working_sol = [];
diagnostic.solvertime = diagnostic.solvertime + toc(solvertime);
if flag == 1
    % This was infeasible, hernce we can use it as an upper bound
    i = 1;
    while flag
        bestUpper = lower;
        lower = lower - 2^(-4+i);i = i+1;
        if options.verbose;
            fprintf([' (fail), ' num2str(lower*the_sign)]);
        end
        try
            solvertime = tic;
            [sol, flag] = P{lower};
            diagnostic.solvertime = diagnostic.solvertime + toc(solvertime);
            if lower < -1e6
                  if options.verbose;
                     fprintf([' (fail). Giving up, never feasible\n']);
                  end
                diagnostic.problem = 21;
                diagnostic.info = yalmiperror(diagnostic.problem,'BISECTION');
                return
            end
        catch
            diagnostic.problem = 1;
            diagnostic.info = yalmiperror(diagnostic.problem,'BISECTION');
            return
        end
    end
    if options.verbose;
        fprintf([' (ok).']);
    end
elseif flag == 0
   working_sol = sol;
else
	diagnostic.problem = flag;
	diagnostic.info = yalmiperror(diagnostic.problem,'BISECTION');
    return
end

v = sol;
optimal = lower;
upper = bestUpper;

if isinf(upper)
    upper = lower+1;
    if options.verbose;
       fprintf([' (ok), ' num2str(upper*the_sign)]);
    end
    [sol, flag] = P{upper};
    i = 1;
    while ~flag
        % This was feasible, so we can use it as new lower bound
        lower = upper;working_sol = sol;optimal = upper;
        % Increase upper bound
        upper = upper + 2^i;i = i+1;
        try
            solvertime = tic;
            if options.verbose;
                fprintf([' (ok), ' num2str(upper*the_sign)]);
            end
            [sol, flag] = P{upper};
            diagnostic.solvertime = diagnostic.solvertime + toc(solvertime);
        catch
            upper
            diagnostic.problem = -1;
            diagnostic.info = yalmiperror(diagnostic.problem,'BISECTION');
            return
        end
        if upper > 1e6
            if options.verbose;
                     fprintf([' (oj). Giving up, always feasible!\n']);
            end
            diagnostic.problem = 2;
            diagnostic.info = yalmiperror(diagnostic.problem,'BISECTION');
            return
        end
    end
    if options.verbose;
        fprintf([' (fail).']);
    end
end

if options.verbose
    fprintf(['\n']);
end

% Perform bisection
iter = 1;
if options.verbose
    disp('Iteration  Lower bound    Test           Upper bound    Gap          Solver status at test');
end
while upper - lower > options.bisection.absgaptol
    test = (upper + lower)/2;
    solvertime = tic;
    [sol, flag] = P{test};
    diagnostic.solvertime = diagnostic.solvertime + toc(solvertime);
    if options.verbose;
        if options.bisection.switchedsign
            L = -upper;
            T = -test;
            U = -lower;
        else
            L = lower;
            T = test;
            U = upper;
        end
        if flag
           if flag~=1 && ~any(isnan(sol))
                assign(x,sol);assign(Objective,test);res = check(Constraints);
                if min(res) >= -1e-6
                  fprintf(' %4.0f :   %12.5E   %12.5E   %12.5E   %12.5E  %s\n',iter,L,T,U,U-L,[yalmiperror(flag) '(looks ok)'] );
                  flag = 0;
                else
                  fprintf(' %4.0f :   %12.5E   %12.5E   %12.5E   %12.5E  %s\n',iter,L,T,U,U-L,[yalmiperror(flag) '(assumed infeasible)']);
                end
           else
            fprintf(' %4.0f :   %12.5E   %12.5E   %12.5E   %12.5E  %s\n',iter,L,T, U,U-L,yalmiperror(flag));
           end
        else
            fprintf(' %4.0f :   %12.5E   %12.5E   %12.5E   %12.5E  %s\n',iter,L,T, U,U-L,yalmiperror(flag));
        end
    end
    if flag == 0
       working_sol = sol;
       optimal = test;
       lower = test;
    else
        upper = test;
    end
    iter = iter + 1;
end
if options.bisection.switchedsign
    optimal = -optimal;
end
if isempty(working_sol)
    diagnostic.problem = 1;
else
    % Assign computed solution
    assign(x,working_sol);
    assign(Objective,optimal);
end
if options.verbose
    if diagnostic.problem==0
        disp(['Bisection terminated successfully with objective ' num2str(optimal)]);
    end
end


function [matchedPoints1,matchedPoints2]=matchFeaturePoints(I1,I2,thresh)
imagePoints1 = detectMinEigenFeatures(rgb2gray(I1), 'MinQuality', thresh);
tracker = vision.PointTracker('MaxBidirectionalError', 1, 'NumPyramidLevels', 5);
imagePoints1 = imagePoints1.Location;
initialize(tracker, imagePoints1, I1);
[imagePoints2, validIdx] = step(tracker, I2);
matchedPoints1 = imagePoints1(validIdx, :);
matchedPoints2 = imagePoints2(validIdx, :);
end


function [lin_restr,nonlin_restr,tpl]=create_restrictions_and_markov_chains0(tpl)
% create_restrictions_and_markov_chains0 -- creates restrictions for
% the constant-parameter SVAR model
%
% ::
%
%
%   [lin_restr,nonlin_restr,tpl]=create_restrictions_and_markov_chains0(tpl)
%
% Args:
%
%    - **tpl** [struct]: template created for SVAR objects
%
% Returns:
%    :
%
%    - **lin_restr** [cell]: two column-cell (see below). The first column
%    contains COEF objects or linear combinations of COEF objects, which are
%    themselves COEF objects.
%
%    - **nonlin_restr** [cell]: one column-cell containing inequality or
%    nonlinear restrictions
%
%    - **tpl** [struct]: non-modified template
%
% Note:
%
%    - The syntax to construct a basic COEF object is a=coef(eqtn,vname,lag)
%      - **eqtn** [integer|char]: integer or variable name
%      - **vname** [integer|char]: integer or variable name
%      - **lag** [integer]: integer or variable name
%
%    - RISE sorts the endogenous variables alphabetically and use this order
%    to tag each equation in SVAR and RFVAR models.
%
% Example:
%    coef('pi','ygap',0)
%
%    coef(2,3,0)
%
%    coef(2,'ygap',0)
%
%    coef('pi',3,0)
%
%    See also:

% syntax is coef(eqtn,vname,lag)
%-------------------------------
lin_restr={
    % first equation or "FFR" equation
    %----------------------------------
    coef('FFR','pi',1),0
    coef('FFR','pi',2),0
    coef('FFR','ygap',1),0
    coef('FFR','ygap',2),0
    coef('FFR','FFR',2),0
    % second equation or "pi" equation
    %----------------------------------
    coef('pi','FFR',0),0
    coef('pi','FFR',1),0
    coef('pi','FFR',2),0
    coef('pi','ygap',1),0
    coef('pi','ygap',2),0
    % third equation or "ygap" equation
    %-----------------------------------
    coef('ygap','FFR',1),0
    coef('ygap','FFR',2),0
    coef('ygap','pi',1),0
    coef('ygap','pi',2),0
    coef('ygap','pi',0)+coef('ygap','FFR',0),0
    };
nonlin_restr={
    % third equation or "ygap" equation
    %-----------------------------------
    'coef(ygap,FFR,0)>=0'
    };
end


%CHEBFUN3V   Class constructor for CHEBFUN3V objects.
%
%   CHEBFUN3V(F, G) constructs a CHEBFUN3V with two components from the
%   function handles F and G. F and G can also be CHEBFUN3 objects or any
%   other object that the CHEBFUN3 constructor accepts. Each component is
%   represented as a CHEBFUN3.
%
%   CHEBFUN3V(F, G, H) constructs a CHEBFUN3V with three components from
%   the function handles F, G, and H.  F, G, and H can also be CHEBFUN3
%   objects or any other objects that the CHEBFUN3 constructor accepts.
%
%   CHEBFUN3V(F, G, [A B C D E K]) constructs a CHEBFUN3V object from F and
%   G on the domain [A B] x [C D] x [E K].
%
%   CHEBFUN3V(F, G, H, [A B C D E K]) constructs a CHEBFUN3V object from F,
%   G, and H on the domain [A B] x [C D] x [E K].
%
% See also CHEBFUN3.

% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

classdef chebfun3v

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %% CLASS PROPERTIES:
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    properties ( Access = public )
        components   % Array of CHEBFUN3 objects.
        nComponents  % Number of components
        isTransposed % transposed?
    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %% CLASS CONSTRUCTOR:
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    methods ( Access = public, Static = false )

        function F = chebfun3v(varargin)
            % The main CHEBFUN3V constructor!

            % Return an empty CHEBFUN3V:
            if ( (nargin == 0) || isempty(varargin) )
                return
            end

            % This function calls the CHEBFUN3 constructor once for each
            % non-zero component because a CHEBFUN3V is just vector of
            % CHEBFUN3 objects.

            % If argument is a CHEBFUN3V, nothing to do:
            if ( isa(varargin{1}, 'chebfun3v') )
                F = varargin{1};
                return
            end

            % Find the domain:
            domain = [-1 1 -1 1 -1 1];
            for jj = 1:numel(varargin)
               if ( isa(varargin{jj}, 'double') && numel(varargin{jj}) == 6 )
                   domain = varargin{jj};
                   varargin(jj) = [];
               elseif ( isa( varargin{jj}, 'chebfun3') )
                   domain = varargin{jj}.domain;
               end
            end

            % Pick up vectorize flag:
            vectorize = 0;
            for jj = 1:numel(varargin)
                if ( strcmpi( varargin{jj}, 'vectorize' ) )
                    vectorize = 1;
                    varargin(jj) = [];
                end
            end

            % Unwrap input arguments;
            component = 1;
            for jj = 1:numel(varargin)
                if ( iscell(varargin{jj}) )
                    for kk = 1:numel(varargin{jj})
                        fh{component} = varargin{jj}{kk};
                        component = component + 1;
                    end
                else
                    fh{component} = varargin{jj};
                    component = component + 1;
                end
            end
            varargin = fh;

            % Convert all function handles to chebfun3 objects:
            for jj = 1:numel(varargin)
                if ( isa( varargin{jj}, 'function_handle') )
                    if ( ~vectorize )
                        newcheb = chebfun3(varargin{jj}, domain);
                    else
                        newcheb = chebfun3(varargin{jj}, domain, 'vectorize');
                    end
                    fh{jj} = newcheb;
                elseif ( isa(varargin{jj}, 'chebfun3') )
                    fh{jj} = varargin{jj};
                elseif ( isa(varargin{jj}, 'double') )
                    fh{jj} = chebfun3(varargin{jj}, domain);
                end
            end

            % Stop if there are too many components
            if ( numel( fh ) > 3 )
                error('CHEBFUN:CHEBFUN3V:arrayValued', ...
                          'More than three components is not supported.')
            end

            % Stop if there are no components:
            if ( numel(fh) == 0 )
                error('CHEBFUN:CHEBFUN3V:empty', ...
                ['The Chebfun3 constructor needs to be given function ' ...
                     'handles or chebfun3 objects.'])
            end

            % Check the domains of all the chebfun3 objects are the same:
            pass = zeros(numel(fh)-1, 1);
            for jj = 2:numel(fh)
               pass(jj-1) = domainCheck(fh{1}, fh{jj});
            end

            if ( ~all(pass) )
                error('CHEBFUN:CHEBFUN3V:domainCheck', ...
                    'All chebfun3 objects need to have the same domain.');
            end

            % Assign to the Chebfun3v object:
            F.components = fh;
            F.nComponents = numel(fh);
            F.isTransposed = 0;

        end
    end

end


function this = parse_input_parameter(this, param)
%PARSE_INPUT_PARAMETER parses and set parameters in the struct PARAM
%
%   This is a member function of the class 'hs_optical_flow'.

%   Author: Deqing Sun, Department of Computer Science, Brown University
%   Contact: dqsun@cs.brown.edu
%   $Date: 2007-11-30 $
%   $Revision: $
%
% Copyright 2007-2010, Brown University, Providence, RI. USA
%
%                          All Rights Reserved
%
% All commercial use of this software, whether direct or indirect, is
% strictly prohibited including, without limitation, incorporation into in
% a commercial product, use in a commercial service, or production of other
% artifacts for commercial purposes.
%
% Permission to use, copy, modify, and distribute this software and its
% documentation for research purposes is hereby granted without fee,
% provided that the above copyright notice appears in all copies and that
% both that copyright notice and this permission notice appear in
% supporting documentation, and that the name of the author and Brown
% University not be used in advertising or publicity pertaining to
% distribution of the software without specific, written prior permission.
%
% For commercial uses contact the Technology Venture Office of Brown University
%
% THE AUTHOR AND BROWN UNIVERSITY DISCLAIM ALL WARRANTIES WITH REGARD TO
% THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
% FITNESS FOR ANY PARTICULAR PURPOSE.  IN NO EVENT SHALL THE AUTHOR OR
% BROWN UNIVERSITY BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
% DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
% PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
% ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
% THIS SOFTWARE.

if mod(length(param), 2) ~=0
    error('Parse_input_parameter: Input parameters must be given in pairs (name and value)!');
end;

i = 1;

while (i <= length(param))

    if ischar(param{i+1})
        param{i+1} = str2num(param{i+1});
    end;

    switch lower(param{i})

        case 'lambda'
            this.lambda         = param{i+1};

        case 'pyramid_levels'
            this.pyramid_levels  = param{i+1};

        case 'pyramid_spacing'
            this.pyramid_spacing = param{i+1};

        case 'max_warping_iters'
            this.max_warping_iters       = param{i+1};

        case 'median_filter_size'
            this.median_filter_size = [param{i+1} param{i+1}];

        case 'image_filter'
            if strcmpi(param{i+1}, 'gaussian')
                this.image_filter = fspecial('gaussian', [15 15], 0.4);
            elseif strcmpi(param{i+1}, 'LoG')
                this.image_filter = - fspecial('gaussian', [15 15], 1);
                this.image_filter(8,8) = 1 + this.image_filter(8,8);
            elseif strcmpi(param{i+1}, 'DoG')
                this.image_filter =  fspecial('gaussian', [15 15], 0.4) - fspecial('gaussian', [15 15], 1);
            else
                this.image_filter = [];
            end;
    end;

    i = i+2;
end;


function varargout = Sim_MonteCarlo_Diffusion_GUI(varargin)
% ## Purpose
% This Monte Carlo simulator for 2D diffusion is able to generate synthetic diffusion signal from any 2D axon packing.
%
% ## Approach
% First, the [axonpacking](https://github.com/neuropoly/axonpacking) software is used to generate dense packing of circles.
% Then, the Monte Carlo simulator moves water molecules around in the packing and apply the phase shift induced by the diffusion gradients.
%
% ## Interface
% First step is to generate an axon packing. Pre-packed examples are
% proposed.
%
% Then, when clicking on `Update` button, the Monte Carlo simulation runs in this packing.
% _Note: blues dots correspond to extra-axonal water molecules and red dots to intra-axonal water_
% _Note: The `MPG strength` and `Signal Strength` plots correspond to the last bvalue in the protocol. But Signal Strength is computed for all bvalues from the protocol._
%
% Once the simulation is finished, synthetic data are plotted and fitted by the charmed model.
% The user is able to save this synthetic signal in a `.mat` file.
%
% Authors: Yasuhiko Tachibana, Tanguy Duval, Tom Mingasson, 2019

% Edit the above text to modify the response to help Sim_SimMCdiff

% Last Modified by GUIDE v2.5 26-Aug-2019 15:37:40

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
    'gui_Singleton',  gui_Singleton, ...
    'gui_OpeningFcn', @Sim_SimMCdiff_OpeningFcn, ...
    'gui_OutputFcn',  @Sim_SimMCdiff_OutputFcn, ...
    'gui_LayoutFcn',  [] , ...
    'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT

% --- Executes just before Sim_SimMCdiff is made visible.
function Sim_SimMCdiff_OpeningFcn(hObject, eventdata, handles, varargin)
handles.output = hObject;
handles.Model = varargin{1};
setappdata(0,'Model',handles.Model);

if ~isfield(handles,'opened')
    if ~ismac
        set(findobj(hObject,'Type','uicontrol'),'FontSize',7);
    end % everything is bigger on windows or linux

    savedPacks = dir(fullfile(fileparts(which('Sim_MonteCarlo_Diffusion_GUI.m')),'savedPacks','*.mat'));
    set(handles.preset_packing,'String',{savedPacks.name});
    [axons, packing] = loadPreset(handles);
    handles.axonpacking.axons = axons;
    handles.axonpacking.packing = packing;

    % Statistics from the packing
    k=1;
    [FVF, FR, MVF, AVF] = compute_statistics(axons.d{k}/2, axons.Delta{k}, packing.final_positions{k}, [], axons.g_ratio{k});
    set(handles.tableVolumes,'Data',[FVF; FR; MVF; AVF])

    % set values
    slider_Naxons_Callback(handles.slider_Naxons, [], handles)
    slider_dvar_Callback(handles.slider_dvar, [], handles)
    slider_dmean_Callback(handles.slider_dmean, [], handles)
    slider_gap_Callback(handles.slider_gap, [], handles)
    slider_trans_Callback(handles.slider_trans, [], handles)
    slider_numelparticle_Callback(handles.slider_numelparticle, [], handles)
    slider_Dcoef_Callback(handles.slider_Dcoef, [], handles)

    % clear axe
    handles.opened = 1;
end
% Update handles structure
guidata(hObject, handles);

% UIWAIT makes Sim_SimMCdiff wait for user response (see UIRESUME)
% uiwait(handles.Simu);

% --- Outputs from this function are returned to the command line.
function varargout = Sim_SimMCdiff_OutputFcn(hObject, eventdata, handles)
% varargout  cell array for returning output args (see VARARGOUT);
% hObject    handle to figure

% Get default command line output from handles structure
varargout{1} = handles.output;

% --- Executes on button press in SimMCdiffUpdate.
function SimMCdiffUpdate_Callback(hObject, eventdata, handles)
set(findobj('Name','SimMCdiff'),'pointer', 'watch'); drawnow;
MonteCarloSim(handles, handles.axonpacking.axons, handles.axonpacking.packing)
set(findobj('Name','SimMCdiff'),'pointer', 'arrow'); drawnow;

% GETAPPDATA
function varargout = GetAppData(varargin)
for k=1:nargin; varargout{k} = getappdata(0, varargin{k}); end

%SETAPPDATA
function SetAppData(varargin)
for k=1:nargin; setappdata(0, inputname(k), varargin{k}); end

% RMAPPDATA
function RmAppData(varargin)
for k=1:nargin; rmappdata(0, varargin{k}); end

% --------------------------------------------------------------------
function helpbutton_ClickedCallback(hObject, eventdata, handles)
doc Sim_MonteCarlo_Diffusion_GUI

function slider_gap_Callback(hObject, eventdata, handles)
%update_axonSetup(handles);
set(handles.text_gap,'String', ['Gap between axons: ' num2str(get(hObject,'Value')) 'um'])
function slider_dvar_Callback(hObject, eventdata, handles)
update_axonSetup(handles);
set(handles.text_dvar,'String', ['Diameter variance: ' num2str(get(hObject,'Value')) 'um'])
function slider_dmean_Callback(hObject, eventdata, handles)
update_axonSetup(handles);
set(handles.text_dmean,'String', ['mean diameter: ' num2str(get(hObject,'Value')) 'um'])
function slider_Naxons_Callback(hObject, eventdata, handles)
update_axonSetup(handles);
set(handles.text_Naxons,'String', ['# axons: ' num2str(round(get(hObject,'Value')))])

function [d, x0, side, axons] = update_axonSetup(handles)
k=1;
axons.N{k}      = round(get(handles.slider_Naxons,'Value'));
axons.d_mean{k} = round(get(handles.slider_dmean,'Value')*10)/10;
set(handles.slider_dmean,'Value',axons.d_mean{k})
axons.d_var{k}  = round(get(handles.slider_dvar,'Value')*10)/10;
set(handles.slider_dvar,'Value',axons.d_var{k});
axons.Delta{k}  = get(handles.slider_gap,'Value');
axons.threshold_high{k} = 20;
axons.threshold_low{k}  = .1;
axons
[d, x0, side] = axons_setup(axons,'gamma', k, handles.axes_axonDist);
if ~ismac, set(handles.axes_axonDist,'FontSize',7); end

function run_pack_Callback(hObject, eventdata, handles)
k=1;
[d, x0, side, axons] = update_axonSetup(handles);

axons.d{k} = d;
axons.g_ratio{k} = compute_gratio(d);

% packing process of the axons
iter_max = 10000;
iter_fvf = iter_max/10;
[final_positions, final_overlap, fvf_historic] = process_packing(x0, d/2, axons.Delta{k}, side, iter_max, iter_fvf);

% store packing results
% main results
packing.initial_positions{k}    = reshape(x0,2,length(x0)/2);
packing.final_positions{k}      = final_positions;
% secondary results
packing.final_overlap{k}        = final_overlap;
packing.FVF_historic{k}         = fvf_historic;
packing.iter_max{k}             = iter_max;

% Statistics from the packing
[FVF, FR, MVF, AVF] = compute_statistics(axons.d{k}/2, axons.Delta{k}, packing.final_positions{k}, side, axons.g_ratio{k});
set(handles.tableVolumes,'Data',[FVF; FR; MVF; AVF])

if ishandle(201), close(201); end
plotPacking(handles,axons,packing)
handles.axonpacking.axons = axons;
handles.axonpacking.packing = packing;

guidata(hObject, handles);

function save_pack_Callback(hObject, eventdata, handles)
axons   = handles.axonpacking.axons;
packing = handles.axonpacking.packing;
fname = sprintf('pack_d%.1fvar%.1fgap%.1f.mat',axons.d_mean{1},axons.d_var{1},axons.Delta{1});
fname = fullfile(fileparts(which('Sim_MonteCarlo_Diffusion_GUI.m')),'savedPacks',fname);
[fname, path] = uiputfile('*.mat','Save Packing as...',fname);
if fname
    save(fullfile(path,fname),'axons','packing')
    savedPacks = get(handles.preset_packing,'String');
    set(handles.preset_packing,'String',unique([savedPacks; {fname}]));
    savedPacks = get(handles.preset_packing,'String');
    set(handles.preset_packing,'Value',length(savedPacks));
end

function preset_packing_Callback(hObject, eventdata, handles)
[axons, packing] = loadPreset(handles);
handles.axonpacking.axons = axons;
handles.axonpacking.packing = packing;
k=1;
[FVF, FR, MVF, AVF] = compute_statistics(axons.d{k}/2, axons.Delta{k}, packing.final_positions{k}, [], axons.g_ratio{k});
set(handles.tableVolumes,'Data',[FVF; FR; MVF; AVF])

guidata(hObject, handles);

function     [axons, packing] = loadPreset(handles)
file = get(handles.preset_packing,'String');
load(file{get(handles.preset_packing,'Value')})
axons_setup(axons,'gamma', 1, handles.axes_axonDist);
if ~ismac, set(handles.axes_axonDist,'FontSize',7); end
plotPacking(handles,axons,packing)

function plotPacking(handles,axons,packing)
% plot disks
numelobj = size(packing.final_positions{1},2);
cen = packing.final_positions{1}'; cen=cen(:,[2 1]); cen=cen-repmat([mean(cen(:,1)) mean(cen(:,2))],[size(cen,1) 1]); % center position of the cells
cen = [cen, zeros(numelobj,1)];

% Radius of the "cells"
R = [axons.d{1}.*axons.g_ratio{1} axons.d{1}]/2; % internal / external diameter in um
R = R(:,2); %use external radius only.

t = linspace(0,2*pi);

axes(handles.axes_axonPack)
hold off
for k =1:size(R,1)
    plot(R(k,1)*cos(t)+cen(k,1), R(k)*sin(t)+cen(k,2),'b','linewidth',1);
    hold on
end
hold off
axis equal tight
xlabel x(\mum)
ylabel y(\mum)

if ~ismac, set(handles.axes_axonPack,'FontSize',7); end

function MonteCarloSim(handles, axons, packing)

% Read updated Model
Model_new = getappdata(0,'Model');
if ~isempty(Model_new) && strcmp(class(Model_new),class(handles.Model))
    handles.Model = Model_new;
end

axes(handles.uipanel12);

% Read parameters
numelparticle = round(get(handles.slider_numelparticle,'Value'));

trans_mean = get(handles.slider_trans,'Value'); % mean probability of penetrating the cell walls [0-1]

D = get(handles.slider_Dcoef,'Value'); % Diffusion coefficient

handles.Model.Sim_MonteCarlo_Diffusion(numelparticle, trans_mean, D, packing, axons);

function slider_trans_Callback(hObject, eventdata, handles)
set(handles.text_trans,'String', ['Permeability: ' num2str(get(hObject,'Value'))])
function slider_numelparticle_Callback(hObject, eventdata, handles)
set(hObject,'Value',round(get(hObject,'Value')))
set(handles.text_numelparticle,'String', ['Number of particles: ' num2str(get(hObject,'Value'))])
function slider_bv_Callback(hObject, eventdata, handles)
set(handles.text_bv,'String', ['bvalue: ' num2str(get(hObject,'Value')) 'sec/mm2'])
function slider_Dcoef_Callback(hObject, eventdata, handles)
D = get(hObject,'Value');
set(handles.text_Dcoef,'String', sprintf('Diffusion coefficient: \n\tD = %.2gx 10-3 mm2/sec',D))
set(handles.text_Step,'String', sprintf('steptime = %.1g [ms]\nstepflight = %.1g [um]\n(stepflight^2 = 4*D*steptime)',0.5,sqrt(4*D*.5)))

function preset_packing_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end
function slider_gap_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end
function slider_dvar_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end
function slider_dmean_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end
function slider_Naxons_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end
function slider_trans_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end
function slider_numelparticle_CreateFcn(hObject, eventdata, handles)
function slider_bv_CreateFcn(hObject, eventdata, handles)
function slider9_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end
function slider_Dcoef_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end


function outstr = midrad(c,name,restricted)
%MIDRAD       Display of intervals by midpoint/radius (rigorous)
%
%The call
%
%  midrad(c)        displays interval c in < mid , rad > representation
%  str = midrad(c)  puts output into string str
%
%Output in string str columnwise; can be used for input by intval(str).
%

%for internal use:
%  name                name of output variable
%  restricted == 1     no header, no extra lines output
%
%Call only with 1 or 3 input arguments
%
%Special call:  outstr = midrad(x,[],[])
%  for column vector x output in outstr.exp and outstr.str
%

% written  11/30/98     S.M. Rump
% modified 06/10/98     S.M. Rump  multi-dimensional arrays, exceptions
% modified 06/23/99     S.M. Rump  dble2str -> dble2str_rnd in \private,
%                                  sparse arrays
% modified 08/29/00     S.M. Rump  special output in outstr.exp and .str added
%                                  rounding unchanged after use
% modified 10/03/02     S.M. Rump  improved for Matlab 6.1f and sparse output
% modified 04/04/04     S.M. Rump  set round to nearest for safety
%                                    take care of Matlab sparse Inf/NaN bug
% modified 04/06/05     S.M. Rump  rounding unchanged
% modified 11/20/05     S.M. Rump  fast check for rounding to nearest
% modified 02/11/06     S.M. Rump  SparseInfNanFlag removed
%                                    linear indices for huge matrices
% modified 08/25/07     S.M. Rump  huge indices for sparse matrices
% modified 10/14/08     S.M. Rump  huge indices for sparse matrices
% modified 08/26/12     S.M. Rump  global variables removed
% modified 10/04/12     S.M. Rump  take care of sparse input
%

  e = 1e-30;
  if 1+e==1-e                           % fast check for rounding to nearest
    rndold = 0;
  else
    rndold = getround;
    setround(0)
  end

  if nargin<=2
    name = inputname(1);
    restricted = 0;
  end

  loose = strcmp(get(0,'FormatSpacing'),'loose');

  siz = size(c);
  if ~restricted & ~nargout & ( length(siz)<3 )
    if loose, disp(' '); end
    disp([ 'intval ' name ' = ' ])
    if loose, disp(' '); end
  end

  if isempty(c)          % empty interval
    if c.complex
      c.mid
    else
      c.inf
    end
    if loose & ~restricted & ~nargout
      disp(' ')
    end
    setround(rndold)
    return
  end

  if length(siz)>2 & ~nargout
    siz = siz(3:end);
    len = length(siz);
    prodsiz = cumprod([1 siz(1:len-1)]);

    for k=0:prod(siz)-1
      y = mod(floor(k./prodsiz),siz);
      str = sprintf(',%d',y+1);
%VVVV eval(['cs = c(:,:' str ');']);
      s.type = '()';
      eval(['s.subs = {'':'','':''' str '};']);
      cs = subsref(c,s);
%AAAA Matlab V5.2 bug fix
      midrad(cs,[name '(:,:' str ')'],0);
    end
    setround(rndold)
    return
  end

  cmid = mid(c);
  crad = rad(c);
  [m n] = size(cmid);

  format = get(0,'format');
  if ~isequal(format,'short') & ~isequal(format,'long') & ...
     ~isequal(format,'shortE') & ~isequal(format,'longE')
    format = 'short';
  end

  INTLAB_INTVAL_POWER10 = getappdata(0,'INTLAB_INTVAL_POWER10');
  commonexp = 0;
  if ~isequal(format(end),'E')
    % calculate exponent range and common factor
    if c.complex
      cre = abs(real(cmid));
      cim = abs(imag(cmid));
      cre = nonzeros(cre);
      cim = nonzeros(cim);
      if isempty(cre)                   % careful: max(1,[]) = []
        cre = 0;
      end
      if isempty(cim)
        cim = 0;
      end
      cre(isinf(cre)) = 0;
      cim(isinf(cim)) = 0;
      cremax = max(cre(:));
      cimmax = max(cim(:));
      cmax = max(cremax,cimmax);
    else
      cmid_ = abs(cmid);
      cmid_ = nonzeros(cmid_);
      if isempty(cmid_)                 % careful: max(1,[]) = []
        cmid_ = 0;
      end
      cmid_(isinf(cmid_)) = 0;
      cmax = max(cmid_);
      cmax = max(cmax(:));
    end
    crad_ = abs(crad);
    crad_ = nonzeros(crad_);
      if isempty(crad_)                 % careful: max(1,[]) = []
        crad_ = 0;
      end
    crad_(isinf(crad_)) = 0;
    cmax = max( cmax , max(crad_(:)) );
    % care for sparse matrices
    cmax = full(cmax);
    if isempty(cmax)
      cmax = 0;
    end

    emax = floor(log10( cmax + (cmax==0) ));
    if c.complex
      if emax >= 2
        commonexp = emax;
      end
      if emax <= -3
        commonexp = emax+1;
      end
    else
      if isequal(format,'short') & emax >= 3
        commonexp = emax;
      elseif isequal(format,'long') & emax >= 2
        commonexp = emax;
      end
      if emax <= -4
        commonexp = emax+1;
      end
    end
  end
  factor = INTLAB_INTVAL_POWER10.sup(1,commonexp+341);    % 10^commonexp

  if commonexp~=0                      % common factor
    if nargout                         % output to str: be sure with exponent
      if ~( isempty(name) & isempty(restricted) )   % if not special output
        commonexp = 0;
        if ~isequal(format(end),'E')
          format = [ format 'E' ];
        end
      end
    else
      strexp = sprintf('%04d',commonexp);
      if commonexp >= 0
        strexp(1) = '+';
      end
      disp([ '  1.0e' strexp ' *' ])
      if loose, disp(' '); end
    end
  end

  if c.complex
    switch format
      case 'short',  len = 9;  prec = 4;  expon = 0;
      case 'long',   len = 19; prec = 14; expon = 0;
      case 'shortE', len = 13; prec = 4;  expon = 1;
      case 'longE',  len = 24; prec = 15; expon = 1;
    end
    len1 = 3*len+5;    % length of one element in current format
  else
    switch format
      case 'short',  len = 10; prec = 4;  expon = 0;
      case 'long',   len = 19; prec = 14; expon = 0;
      case 'shortE', len = 13; prec = 4;  expon = 1;
      case 'longE',  len = 24; prec = 15; expon = 1;
    end
    len1 = 2*len+2;    % length of one element in current format
  end
  INTLAB_DISPLAY_WIDTH = getappdata(0,'INTLAB_DISPLAY_WIDTH');    % minimum 110, so columns>=1
  columns = floor((INTLAB_DISPLAY_WIDTH+1)/(len1+1));

  formatstr = [ '%' int2str(len) '.' int2str(prec) 'f' ];
  if expon
    formatstr(end) = 'e';
  end

  if nargout & isempty(name) & isempty(restricted)
    % special case: input c column vector, output in outstr.exp and outstr.str

    if commonexp~=0                      % common factor
      strexp = sprintf('%04d',commonexp);
      if commonexp >= 0
        strexp(1) = '+';
      end
      outstr.exp = [ '  1.0e' strexp ' *' ];
    else
      outstr.exp = '';
    end

    if c.complex           % complex intervals
      outstr.str = char(zeros(m,3*len+5));
      for i=1:m
        s = '';
        cmidi = full(cmid(i));
        strrealmid = sprintf(formatstr,real(cmidi)/factor);
        signimagmid = sign(imag(cmidi));
        strimagmid = sprintf(formatstr,abs(imag(cmidi))/factor);
        strimagmid = strimagmid(2:end);     % omit leading space
        [cmidrealinf,cmidrealsup] = str2intval(strrealmid,commonexp);
        if signimagmid==1
          [cmidimaginf,cmidimagsup] = str2intval(strimagmid,commonexp);
        else
          [cmidimagsup,cmidimaginf] = str2intval(strimagmid,commonexp);
          cmidimagsup = - cmidimagsup ;
          cmidimaginf = - cmidimaginf ;
        end
        setround(1)
        re = max( real(cmidi)-cmidrealinf,cmidrealsup-real(cmidi) );
        im = max( imag(cmidi)-cmidimaginf,cmidimagsup-imag(cmidi) );
        if isequal(crad,0)
          cradij = abs( re + sqrt(-1)*im );
        else
          cradij = crad(i) + abs( re + sqrt(-1)*im );
        end
        cradij = full(cradij);
        chsign = '+';
        if signimagmid==-1
          chsign = '-';
        end
        strrad = dble2str_rnd(cradij,commonexp,len-1,prec,expon,+1);
        s = [ s '<' strrealmid ' ' chsign strimagmid 'i,' ...
              strrad '> ' ];
        outstr.str(i,:) = s;
      end
    else                   % real intervals
      outstr.str = char(zeros(m,2*len+3));
      for i=1:m
        s = '';
        cmidi = full(cmid(i));
        strmid = sprintf(formatstr,cmidi/factor);
        [cmidinf,cmidsup] = str2intval(strmid,commonexp);
        setround(1)
        cradij = full( crad(i) + max(cmidi-cmidinf,cmidsup-cmidi) );
        s = [ s '<' strmid ',' ...
              dble2str_rnd(cradij,commonexp,len-1,prec,expon,+1) '> ' ];
        outstr.str(i,:) = s;
      end
    end
    setround(rndold)
    return
  end

  if nargout
    outstr = [];
    if c.complex
      for i=1:prod(size(cmid))
        cmidi = full(cmid(i));
        strrealmid = sprintf(formatstr,real(cmidi));
        strimagmid = sprintf(formatstr,imag(cmidi));
        strimagmid(isspace(strimagmid)) = '';
        if strimagmid(1)~='-'
          strimagmid = [ '+' strimagmid ];
        end
        [cmidrealinf,cmidrealsup] = str2intval(strrealmid,0);
        [cmidimaginf,cmidimagsup] = str2intval(strimagmid,0);
        setround(1)
        re = max( real(cmidi)-cmidrealinf,cmidrealsup-real(cmidi) );
        im = max( imag(cmidi)-cmidimaginf,cmidimagsup-imag(cmidi) );
        if isequal(crad,0)
          cradi = abs( re + sqrt(-1)*im );
        else
          cradi = crad(i) + abs( re + sqrt(-1)*im );
        end
        cradi = full(cradi);
        strrad = dble2str_rnd(cradi,commonexp,len-1,prec,expon,+1);
        outstr = [ outstr '<' strrealmid strimagmid 'i,' strrad '> ' ];
      end
    else
      for i=1:prod(size(cmid))
        cmidi = full(cmid(i));
        strmid = sprintf(formatstr,cmidi);
        [cmidinf,cmidsup] = str2intval(strmid,0);
        setround(1)
        cradi = full( crad(i) + max(cmidi-cmidinf,cmidsup-cmidi) );
        outstr = [ outstr '<' strmid ',' ...
              dble2str_rnd(cradi,commonexp,len-1,prec,expon,+1) '> ' ];
      end
    end
    setround(rndold)
    return
  end

  if issparse(c)
    if isequal(crad,0)                            % mid or rad maybe zero
      [I,J] = find(spones(cmid));
    else
      [I,J] = find(spones(cmid)+spones(crad));
    end
    if length(I)==0
      mid(c)
      setround(rndold)
      return
    end
    if c.complex
      for i=1:length(I)
        str = sprintf('  (%d,%d)',I(i),J(i));
        str = [ str blanks(20-length(str)) ];
        cmidij = full(cmid(I(i),J(i)));
        strrealmid = sprintf(formatstr,real(cmidij)/factor);
        signimagmid = sign(imag(cmidij));
        strimagmid = sprintf(formatstr,abs(imag(cmidij))/factor);
        strimagmid = strimagmid(2:end);     % omit leading space
        [cmidrealinf,cmidrealsup] = str2intval(strrealmid,commonexp);
        if signimagmid==1
          [cmidimaginf,cmidimagsup] = str2intval(strimagmid,commonexp);
        else
          [cmidimagsup,cmidimaginf] = str2intval(strimagmid,commonexp);
          cmidimagsup = - cmidimagsup ;
          cmidimaginf = - cmidimaginf ;
        end
        setround(1)
        re = max( real(cmidij)-cmidrealinf,cmidrealsup-real(cmidij) );
        im = max( imag(cmidij)-cmidimaginf,cmidimagsup-imag(cmidij) );
        if isequal(crad,0)
          cradij = abs( re + sqrt(-1)*im );
        else
          cradij = crad(I(i),J(i)) + abs( re + sqrt(-1)*im );
        end
        cradij = full(cradij);
        chsign = '+';
        if signimagmid==-1
          chsign = '-';
        end
        strrad = dble2str_rnd(cradij,commonexp,len-1,prec,expon,+1);
        str = [ str '<' strrealmid ' ' chsign strimagmid 'i,' ...
                strrad '> ' ];
        disp( str )
      end
    else
      for i=1:length(I)
        str = sprintf('  (%d,%d)',I(i),J(i));
        str = [ str blanks(20-length(str)) ];
        cmidij = full(cmid(I(i),J(i)));
        strmid = sprintf(formatstr,cmidij/factor);
        [cmidinf,cmidsup] = str2intval(strmid,commonexp);
        setround(1)
        cradij = full(crad(I(i),J(i)) + max(cmidij-cmidinf,cmidsup-cmidij));
        str = [ str '<' strmid ',' ...
                dble2str_rnd(cradij,commonexp,len-1,prec,expon,+1) '> ' ];
        disp( str )
      end
    end
    setround(rndold)
    return
  end

  for jj=1:ceil(n/columns)
    j1 = (jj-1)*columns+1;
    if jj*columns<n
      j2 = jj*columns;
    else
      j2 = n;
    end
    if n>columns
      if j1~=j2
        disp(['  Columns ' sprintf('%d',j1) ' through ' sprintf('%d',j2)]);
      else
        disp(['  Column ' sprintf('%d',j1)]);
      end
      if loose, disp(' '); end
    end
    if c.complex           % complex intervals
      for i=1:m
        s = '';
        for j = j1:j2
          cmidij = full(cmid(i,j));
          strrealmid = sprintf(formatstr,real(cmidij)/factor);
          signimagmid = sign(imag(cmidij));
          strimagmid = sprintf(formatstr,abs(imag(cmidij))/factor);
          strimagmid = strimagmid(2:end);     % omit leading space
          [cmidrealinf,cmidrealsup] = str2intval(strrealmid,commonexp);
          if signimagmid==1
            [cmidimaginf,cmidimagsup] = str2intval(strimagmid,commonexp);
          else
            [cmidimagsup,cmidimaginf] = str2intval(strimagmid,commonexp);
            cmidimagsup = - cmidimagsup ;
            cmidimaginf = - cmidimaginf ;
          end
          setround(1)
          re = max( real(cmidij)-cmidrealinf,cmidrealsup-real(cmidij) );
          im = max( imag(cmidij)-cmidimaginf,cmidimagsup-imag(cmidij) );
          if isequal(crad,0)
            cradij = abs( re + sqrt(-1)*im );
          else
            cradij = crad(i,j) + abs( re + sqrt(-1)*im );
          end
          cradij = full(cradij);
          chsign = '+';
          if signimagmid==-1
            chsign = '-';
          end
          strrad = dble2str_rnd(cradij,commonexp,len-1,prec,expon,+1);
          s = [ s '<' strrealmid ' ' chsign strimagmid 'i,' ...
                strrad '> ' ];
        end
        disp(s)
      end
    else                   % real intervals
      for i=1:m
        s = '';
        for j = j1:j2
          cmidij = full(cmid(i,j));
          strmid = sprintf(formatstr,cmidij/factor);
          [cmidinf,cmidsup] = str2intval(strmid,commonexp);
          setround(1)
          cradij = full( crad(i,j) + max(cmidij-cmidinf,cmidsup-cmidij) );
          s = [ s '<' strmid ',' ...
                dble2str_rnd(cradij,commonexp,len-1,prec,expon,+1) '> ' ];
        end
        disp(s)
      end
    end
    if loose & ~restricted, disp(' '); end
  end

  setround(rndold)

function [xinf,xsup] = str2intval(str,commonexp)
%STR2INTVAL   internal routine for output routines midrad
%

%Rigorous conversion of string to lower/upper bound with directed rounding
%  and common exponent; only for one real input.
%Input string in e- or f-format, possibly with exponent
%Input number is assumed to be correct.
%

  % exception handling
  if any( str=='a' ) | any( str=='f' )    % NaN or Inf
    xinf = NaN;
    xsup = NaN;
    return
  end

  INTLAB_INTVAL_POWER10 = getappdata(0,'INTLAB_INTVAL_POWER10');
  [m i] = max(str~=' ');
  str(1:i-1) = '';             % omit leading blanks

  sign = 1;                    % calculate sign
  if str(1)=='+'
    str = str(2:end);
  elseif str(1)=='-'
    sign = -1;
    str = str(2:end);
  end

  indexdp = find(str=='.');    % index of decimal point
  str(indexdp) = '';

  indexexp = find(str=='e');   % check exponent
  if ~isempty(indexexp)
    Exp = ( 2*(str(indexexp+1)=='+') - 1 ) * ...
          (str(indexexp+2:end)-'0')*(10.^((length(str)-indexexp-2):-1:0)');
    indexend = indexexp-1;
  else
    Exp = 0;
    indexend = length(str);
  end

  m = str(indexend:-1:1) - '0';

  % convert back to double
  offset = 9*(indexdp-2+Exp+commonexp+341);
  mant = 9*(indexend:-1:1);
  index = (m~=0).*(offset - mant + m) + (m==0);
  if sign==1
    setround(-1)
    xinf = sum( INTLAB_INTVAL_POWER10.inf(index) );
    setround(1)
    xsup = sum( INTLAB_INTVAL_POWER10.sup(index) );
  else
    setround(1)
    xinf = - sum( INTLAB_INTVAL_POWER10.sup(index) );
    setround(-1)
    xsup = - sum( INTLAB_INTVAL_POWER10.inf(index) );
  end
  setround(0)


function approxhess = getApproxHessian(problem, x, d, storedb, key)
% Computes an approximation of the Hessian of the cost fun. at x along d.
%
% function approxhess = getApproxHessian(problem, x, d)
% function approxhess = getApproxHessian(problem, x, d, storedb)
% function approxhess = getApproxHessian(problem, x, d, storedb, key)
%
% Returns an approximation of the Hessian at x along d of the cost function
% described in the problem structure.
%
% storedb is a StoreDB object, key is the StoreDB key to point x.
%
% If no approximate Hessian was provided, this call is redirected to
% getHessianFD.
%
% See also: getHessianFD canGetApproxHessian

% This file is part of Manopt: www.manopt.org.
% Original author: Nicolas Boumal, Dec. 30, 2012.
% Contributors:
% Change log:
%
%   April 3, 2015 (NB):
%       Works with the new StoreDB class system.

    % Allow omission of the key, and even of storedb.
    if ~exist('key', 'var')
        if ~exist('storedb', 'var')
            storedb = StoreDB();
        end
        key = storedb.getNewKey();
    end

    if isfield(problem, 'approxhess')
    %% Compute the approximate Hessian using approxhess.

        % Check whether this function wants to deal with storedb or not.
        switch nargin(problem.approxhess)
            case 2
                approxhess = problem.approxhess(x, d);
            case 3
                % Obtain, pass along, and save the store for x.
                store = storedb.getWithShared(key);
                [approxhess, store] = problem.approxhess(x, d, store);
                storedb.setWithShared(store, key);
            case 4
                % Pass along the whole storedb (by reference), with key.
                approxhess = problem.approxhess(x, d, storedb, key);
            otherwise
                up = MException('manopt:getApproxHessian:badapproxhess', ...
                    'approxhess should accept 2, 3 or 4 inputs.');
                throw(up);
        end

    else
    %% Try to fall back to a standard FD approximation.

        approxhess = getHessianFD(problem, x, d, storedb, key);

    end

end


%% Analyzing Neural Time Series Data
% Matlab code for Chapter 9
% Mike X Cohen
%
% This code accompanies the book, titled "Analyzing Neural Time Series Data"
% (MIT Press). Using the code without following the book may lead to confusion,
% incorrect data analyses, and misinterpretations of results.
% Mike X Cohen assumes no responsibility for inappropriate or incorrect use of this code.

%% Figure 9.1a

% load EEG data
load sampleEEGdata.mat

% plot a few trials from one channel...

% specify the label of the channel to plot
which_channel_to_plot = 'fcz';
% and find the index (channel number) of that label
channel_index = strcmpi(which_channel_to_plot,{EEG.chanlocs.labels});

x_axis_limit = [-200 1000]; % in ms

num_trials2plot = 12;

figure
set(gcf,'Name',[ num2str(num_trials2plot) ' random trials from channel ' which_channel_to_plot ],'Number','off')
for i=1:num_trials2plot

    % figure out how many subplots we need
    subplot(ceil(num_trials2plot/ceil(sqrt(num_trials2plot))),ceil(sqrt(num_trials2plot)),i)

    % pick a random trial (using randsample, which is in the stats toolbox)
    random_trial_to_plot = randsample(EEG.trials,1);
    % if you don't have the stats toolbox, use the following two lines:
    %random_trial_to_plot = randperm(EEG.trials);
    %random_trial_to_plot = random_trial_to_plot(1);

    % plot trial and specify x-axis and title
    plot(EEG.times,squeeze(EEG.data(channel_index,:,random_trial_to_plot)));
    set(gca,'xlim',x_axis_limit,'ytick',[])
    title([ 'Trial ' num2str(random_trial_to_plot) ])
end

%% Figure 9.1b

figure
% plot all trials
plot(EEG.times,squeeze(EEG.data(channel_index,:,:)),'y')

hold on
% plot ERP (simply the average time-domain signal)
plot(EEG.times,squeeze(mean(EEG.data(channel_index,:,:),3)),'k','linew',2)
set(gca,'xlim',[-300 1000],'ylim',[-60 60],'ydir','reverse')

% now plot only the ERP
figure
plot(EEG.times,squeeze(mean(EEG.data(channel_index,:,:),3))) % Note the "3" as second input to "mean"; this takes the average of the 3rd dimension.

% plot lines indicating baseline activity and stim onset
hold on
plot(get(gca,'xlim'),[0 0],'k')
plot([0 0],get(gca,'ylim'),'k:')

% add axis labels and title
xlabel('Time (ms)')
ylabel('\muV') % note that matlab interprets "\mu" as the Greek character for micro
title([ 'ERP (average of ' num2str(EEG.trials) ' trials) from electrode ' EEG.chanlocs(channel_index).labels ])

% plot upside down, following ERP convention
set(gca,'ydir','reverse')
% axis ij % this does the same as the previous trial

% below is some advanced but flexible code to change the x-axis label
set(gca,'xlim',[-300 1000])
xticklabel=cellstr(get(gca,'xticklabel'));
xticklabel{str2double(xticklabel)==0}='stim';
set(gca,'xticklabel',xticklabel)

%% Figure 9.2

% pick a channel
chan2plot = 'p7';

% compute ERP
erp = double(squeeze(mean(EEG.data(strcmpi(chan2plot,{EEG.chanlocs.labels}),:,:),3)));

% low-pass filter data (requires signal processing toolbox)
% you'll learn about how filtering works, and what this code means, in chapter 14.
nyquist          = EEG.srate/2;
transition_width = 0.15; % percent

% first, filter from 0-40
filter_cutoff = 40; % Hz
ffrequencies  = [ 0 filter_cutoff filter_cutoff*(1+transition_width) nyquist ]/nyquist;
idealresponse = [ 1 1 0 0 ];
filterweights = firls(100,ffrequencies,idealresponse);
erp_0to40     = filtfilt(filterweights,1,double(erp));

% next, filter from 0-10
filter_cutoff = 10; % Hz
ffrequencies  = [ 0 filter_cutoff filter_cutoff*(1+transition_width) nyquist ]/nyquist;
idealresponse = [ 1 1 0 0 ];
filterweights = firls(100,ffrequencies,idealresponse);
erp_0to10     = filtfilt(filterweights,1,double(erp));

% finally, filter from 5-15
filter_low    =  5; % Hz
filter_high   = 15; % Hz
ffrequencies  = [ 0 filter_low*(1-transition_width) filter_low filter_high filter_high*(1+transition_width) nyquist ]/nyquist;
idealresponse = [ 0 0 1 1 0 0 ];
filterweights = firls(round(3*(EEG.srate/filter_low)),ffrequencies,idealresponse);
erp_5to15     = filtfilt(filterweights,1,double(erp));

% now plot all filtered ERPs
figure
plot(EEG.times,erp,'k')
hold on
plot(EEG.times,erp_0to40,'b','linew',2)
plot(EEG.times,erp_0to10,'r')
plot(EEG.times,erp_5to15,'m')

set(gca,'xlim',[-200 1200],'ydir','r')
xlabel('Time (ms)'), ylabel('Voltage (\muV)')
title([ 'ERP from electrode ' chan2plot ])
legend({'None';'0-40';'0-10';'5-15'})

%% Figure 9.3

figure

% Butterfly plot
subplot(211)
plot(EEG.times,squeeze(mean(EEG.data,3)))
set(gca,'xlim',[-200 1000],'ydir','reverse')
xlabel('Time (ms)'), ylabel('\muV')
title('ERP from all sensors')

% topographical variance plot
subplot(212)
plot(EEG.times,squeeze(var( mean(EEG.data,3) ))) % note: change var to std for global field power
set(gca,'xlim',[-200 1000])
xlabel('Time (ms)'), ylabel('var(\muV)')
title('Topographical variance')

%% figure 9.4

% topoplot with colored dots vs. interpolated surface
% topoplot is a function in the eeglab toolbox, which you can download for
% free from the internet.

figure
subplot(121)
% To get the following line to work, you need to modify the eeglab function 'topoplot'
% Replace the first line of code (the function definition) with:
% function [handle,Zi,grid,Xi,Yi,x,y,pltchans] = topoplot(Values,loc_file,varargin)
[~,~,~,~,~,xi,yi,pltchans] = topoplot(zeros(64,1),EEG.chanlocs,'electrodes','off','plotrad',.53);

hold on
c = -squeeze(mean(EEG.data(:,300,:),3));
c = c-min(c); % these lines scale the variable c
c = c./max(c);
c = repmat(c,1,3);
for i=1:length(xi)
    hold on
    plot3(yi(i),xi(i),.1,'o','markerfacecolor',c(i,:),'markersize',5,'markeredge','none');
    % here, plot3 is used to plot the electrode on top of the topomap
    % (compare with the plot function) (a trick I learned from topoplot.m)
end

subplot(122)
c = -squeeze(mean(EEG.data(:,300,:),3));
c = c-min(c);
c = c./max(c);
c = repmat(c,1,3);
topoplot(double(c(:,1)),EEG.chanlocs,'plotrad',.53,'electrodes','off','numcontour',0);

colormap gray

%% introduction to topographical plotting

timepoint2plot = 100; % in ms
trial2plot = randsample(EEG.trials,1);

color_limit = 20; % more-or-less arbitrary, but this is a good value

% convert time point from ms to index
[junk,timepointidx] = min(abs(EEG.times-timepoint2plot));

% First step is to get X and Y coordinates of electrodes
% These must be converted to polar coordinates
th=pi/180*[EEG.chanlocs.theta];
[electrode_locs_X,electrode_locs_Y] = pol2cart(th,[EEG.chanlocs.radius]);

% interpolate to get nice surface
interpolation_level = 100; % you can try changing this number, but 100 is probably good
interpX = linspace(min(electrode_locs_X),max(electrode_locs_X),interpolation_level);
interpY = linspace(min(electrode_locs_Y),max(electrode_locs_Y),interpolation_level);

% meshgrid is a function that creates 2D grid locations based on 1D inputs
[gridX,gridY] = meshgrid(interpX,interpY);

% let's look at these matrices
figure
subplot(121)
imagesc(gridX)

subplot(122)
imagesc(gridY)

% now interpolate the data on a 2D grid
interpolated_EEG_data = griddata(electrode_locs_Y,electrode_locs_X,double(squeeze(EEG.data(:,timepointidx,trial2plot))),gridX,gridY);

figure
set(gcf,'number','off','name',[ 'Topographical data from trial ' num2str(trial2plot) ', time=' num2str(round(EEG.times(timepointidx))) ]);
subplot(121)
contourf(interpY,interpX,interpolated_EEG_data,100,'linecolor','none');
axis square
set(gca,'clim',[-color_limit color_limit],'xlim',[min(interpY) max(interpY)]*1.1,'ylim',[min(interpX) max(interpX)]*1.1)
title('Interpolated data in matlab')

subplot(122)
topoplot(double(squeeze(EEG.data(:,timepointidx,trial2plot))),EEG.chanlocs,'maplimits',[-color_limit color_limit]); % eeglab's topoplot function
title('eeglab ''topoplot'' function')

figure
set(gcf,'name','a landscape of cortical electrophysiological dynamics')
surf(interpY,interpX,interpolated_EEG_data);
xlabel('left-right of scalp'), ylabel('anterior-posterior of scalp'), zlabel('\muV')
shading interp, axis tight
set(gca,'clim',[-color_limit color_limit],'xlim',[min(interpY) max(interpY)]*1.1,'ylim',[min(interpX) max(interpX)]*1.1)
rotate3d on, view(0,90)

%% Figure 9.5

% find indices for requested plot times
times2plot = dsearchn(EEG.times',(-100:50:600)');

figure
for i=1:length(times2plot)
    subplot(3,5,i)

    % extract EEG data and replace FC4 data with noise
    eegdata2plot = double(squeeze(mean(EEG.data(:,times2plot(i),:),3)));
    eegdata2plot(strcmpi('fc4',{EEG.chanlocs.labels})) = randn*10;

    topoplot(eegdata2plot,EEG.chanlocs,'maplimits',[-8 8]);
    title([ num2str(round(EEG.times(times2plot(i)))) ' ms' ])
end

%% Figure 9.6

useRTs = true; % or false

% get RTs from each trial, to use for sorting trials. In this experiment,
% the RT was always the first event after the stimulus (the time=0 event).
% Normally, you should build in exceptions in case there was no response or
% another event occured between the stimulus and response. This was already
% done for the current dataset.

rts = zeros(size(EEG.epoch));
for ei=1:length(EEG.epoch)

    % first, find the index at which the time=0 event occurs
    time0event = find(cell2mat(EEG.epoch(ei).eventlatency)==0);

    % then get reaction time
    rts(ei) = EEG.epoch(ei).eventlatency{time0event+1};
end

% now find the sorting indices for RTs
if useRTs
    [dontneed,rts_idx] = sort(rts);
else
    [dontneed,rts_idx] = sort(squeeze(EEG.data(47,334,:)));
end

% now plot
figure
imagesc(EEG.times,1:EEG.trials,squeeze(EEG.data(47,:,rts_idx))');
set(gca,'clim',[-30 30],'xlim',[-200 1200],'ydir','n')

% also plot the RTs on each trial
if useRTs
    hold on
    plot(rts(rts_idx),1:EEG.trials,'k','linew',3)
end

%% end.


% Copyright 2011 Zdenek Kalal
%
% This file is part of TLD.
%
% TLD is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% TLD is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with TLD.  If not, see <http://www.gnu.org/licenses/>.

function bb = bb_shift_relative(bb,shift)
% Change

if isempty(bb)
    return;
end
bb(1,:) = bb(1,:) + bb_width(bb)*shift(1);
bb(2,:) = bb(2,:) + bb_height(bb)* shift(2);
bb(3,:) = bb(3,:) + bb_width(bb)*shift(1);
bb(4,:) = bb(4,:) + bb_height(bb)*shift(2);


function [featureVector,calcTimes,calcQuality] = TS_CalculateFeatureVector(tsStruct,doParallel,Operations,MasterOperations,codeSpecial,howVocal)
% TS_CalculateFeatureVector	Compute a feature vector from an input time series
%
%---INPUTS:
% tsStruct, either (i) a vector of time-series data to compute
% 				or (ii) an hctsa-style table of time-series data
% doParallel, (binary) whether to compute the features using parallel processing
% Operations, an hctsa-style table of Operations
% MasterOperations, an hctsa-style table of MasterOperations
% codeSpecial, whether to code special values with quality labels (for mySQL database)
% 				this makes sure the featureVector is all real numbers, and any
%				special values (like NaNs, Infs, etc.) are coded with corresponding
% 				labels in calcQuality.
% 				codeSpecial = false [default]: special values are kept in the
% 							feature vector, and any errors are coded as NaN.
% 				codeSpecial = true: featureVector is all real numbers, and is set to
% 							     zero where any special-valued outputs occur.
% howVocal, {'fast', 'minimal', or 'full'}: how to give user feedback on the computation.
%
%---OUTPUTS:
% featureVector, the feature vector obtained by running MasterOperations and
% 					retrieving all features defined in the Operations structure
% 					array on the time series data given in tsStruct.
% calcTimes, corresponding calculation times for each feature in featureVector
% calcQuality, quality labels of each calculation (e.g., coding for NaNs, Infs, etc.)
%
%---USAGE:
% Quickly compute a feature vector of a 500-long random time-series using the
% default operation library using parallel processing:
% >> features = TS_CalculateFeatureVector(randn(500,1));

% ------------------------------------------------------------------------------
% Copyright (C) 2020, Ben D. Fulcher <ben.d.fulcher@gmail.com>,
% <http://www.benfulcher.com>
%
% If you use this code for your research, please cite the following two papers:
%
% (1) B.D. Fulcher and N.S. Jones, "hctsa: A Computational Framework for Automated
% Time-Series Phenotyping Using Massive Feature Extraction, Cell Systems 5: 527 (2017).
% DOI: 10.1016/j.cels.2017.10.001
%
% (2) B.D. Fulcher, M.A. Little, N.S. Jones, "Highly comparative time-series
% analysis: the empirical structure of time series and their methods",
% J. Roy. Soc. Interface 10(83) 20130048 (2013).
% DOI: 10.1098/rsif.2013.0048
%
% This work is licensed under the Creative Commons
% Attribution-NonCommercial-ShareAlike 4.0 International License. To view a copy of
% this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ or send
% a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View,
% California, 94041, USA.
% ------------------------------------------------------------------------------

%-------------------------------------------------------------------------------
% Check Inputs
%-------------------------------------------------------------------------------
if isnumeric(tsStruct)
	% Provided data only without metadata:
	tsData = tsStruct;
	tsStruct = struct('Name','Input Timeseries', ...
						'Data',tsData, ...
						'ID',1, ...
						'Length',length(tsData));
elseif istable(tsStruct)
	tsStruct = table2struct(tsStruct);
end

if nargin < 2
    fprintf(1,'Computing features in parallel by default.\n');
	doParallel = true;
end

if nargin < 3 || isempty(Operations)
    fprintf(1,'Importing the default hctsa set of time-series features!\n');
    theINPfile = 'INP_ops_hctsa.txt';
    Operations = SQL_Add('ops',theINPfile,false,false);
elseif ischar(Operations)
    theINPfile = Operations;
    Operations = SQL_Add('ops',theINPfile,false,false);
end
if isnumeric(Operations)
	error('Provide an input file or a structure array of Operations.');
end

if nargin < 4 || isempty(MasterOperations)
	% Use the default library:
	MasterOperations = SQL_Add('mops','INP_mops_hctsa.txt',false,false);
	% Need to link operations to masters if not already supplied:
	[Operations,MasterOperations] = TS_LinkOperationsWithMasters(Operations,MasterOperations);
end

% Whether to code up special-valued outputs
if nargin < 5
	codeSpecial = false;
end

% Whether to give information out to screen
if nargin < 6
	howVocal = 'minimal';
end

%-------------------------------------------------------------------------------
% Check Statistics toolbox is available (needed throughout hctsa, including for
% z-scoring)
%-------------------------------------------------------------------------------
BF_CheckToolbox('statistics_toolbox');

% ------------------------------------------------------------------------------
%% Open parallel processing worker pool
% ------------------------------------------------------------------------------
if doParallel
    % Check that a parallel worker pool is open (if not, attempt to initiate it):
	doParallel = TS_InitiateParallel(false);
end
% Tell the user about it:
if strcmp(howVocal,'full')
    if doParallel
        fprintf(1,['Computation will be performed across multiple cores' ...
                ' using Matlab''s Parallel Computing Toolbox.\n']);
    else % use single-threaded for loops
        fprintf(1,'Computations will be performed serially without parallelization.\n');
    end
end

% --------------------------------------------------------------------------
%% Basic checking on the data
% --------------------------------------------------------------------------
% (x a N x 1 column vector)
x = tsStruct.Data;
if size(x,2) ~= 1
	if size(x,1) == 1
		fprintf(1,['***** The time series %s is a row vector. Not sure how it snuck',...
								' through the processing cracks, but I need' ...
								' a column vector...\n'],tsStruct.Name);
		fprintf(1,'I''ll transpose it for you for now....\n');
		x = x';
	else
		fprintf(1,'******************************************************************************************\n');
		error('ERROR WITH ''%s'' -- is it multivariate or something weird? Skipping!\n',tsStruct.Name);
	end
end
% Basics checks on data type:
if ~isa(x,'numeric')
    error('The time series provided must be numerical data')
elseif isa(x,'integer')
    error('Methods in hctsa are generally not well-suited to integer-valued time-series data. Convert to double.')
elseif isa(x,'single')
    warning('Your data is provided in single precision. Converting to double for compatibility with hctsa methods.')
    x = double(x);
end
% (x contains no special values)
if ~all(isfinite(x))
	error('ERROR WITH ''%s'' -- contains non-finite values',tsStruct.Name);
end
if all(x==x(1))
	warning('Data are a constant -- there is no information to derive from the time series; many features will fail')
end

% --------------------------------------------------------------------------
%% Display information
% --------------------------------------------------------------------------
numCalc = height(Operations); % Number of features to calculate
if numCalc == 0
	error('Nothing to calculate :-/')
end

if strcmp(howVocal,'full')
    fprintf(1,'=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n');
    fprintf(1,'Preparing to calculate %s\nts_id = %u, N = %u samples\nComputing %u features.\n', ...
                    tsStruct.Name,tsStruct.ID,tsStruct.Length,numCalc);
    fprintf(1,'=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n');
end

% Initialize variables:
featureVector = zeros(numCalc,1); % Output of each operation
calcQuality = zeros(numCalc,1); % Quality of output from each operation
calcTimes = nan(numCalc,1); % Calculation time for each operation

% --------------------------------------------------------------------------
%% Pre-Processing
% --------------------------------------------------------------------------
% x_z is a z-scored transformation of the time series:
x_z = zscore(x);

% So we now have the raw time series x and the z-scored time series x_z.
% Operations take these as inputs.

fullTimer = tic;

% --------------------------------------------------------------------------
%% Evaluate all master operation functions (maybe in parallel)
% --------------------------------------------------------------------------
% Because of parallelization, we have to evaluate all the master functions *first*
% Check through the metrics to determine which master functions are relevant for this run

% Put the output from each Master operation in an element of MasterOutput
MasterOutput = cell(height(MasterOperations),1); % Ouput structures
MasterCalcTime = zeros(height(MasterOperations),1); % Calculation times for each master operation

Master_IDs_calc = unique(Operations.MasterID); % Master_IDs that need to be calculated
Master_ind_calc = arrayfun(@(x)find(MasterOperations.ID==x,1),Master_IDs_calc); % Indicies of MasterOperations that need to be calculated
numMopsToCalc = length(Master_IDs_calc); % Number of master operations to calculate

% Index sliced variables to minimize the communication overhead in the parallel processing
par_MasterOpCodeCalc = MasterOperations.Code(Master_ind_calc); % String array of strings of Code to evaluate
par_mop_ids = MasterOperations.ID(Master_ind_calc); % mop_id for each master operation

% Store in temporary variables for parfor loop then map back later
MasterOutput_tmp = cell(numMopsToCalc,1);
MasterCalcTime_tmp = zeros(numMopsToCalc,1);

% ----
% Evaluate all the master operations
% ----
if strcmp(howVocal,'full')
    fprintf(1,'Evaluating %u master operations...\n',length(Master_IDs_calc));
end
TimeSeries_i_ID = tsStruct.ID; % Make a PARFOR-friendly version of the ID
masterTimer = tic;
if doParallel
    % PARFOR Loop (parallel)
	parfor jj = 1:numMopsToCalc
		[MasterOutput_tmp{jj},MasterCalcTime_tmp(jj)] = ...
			TS_ComputeMasterLoop(x,x_z,par_MasterOpCodeCalc{jj}, ...
				par_mop_ids(jj),numMopsToCalc,howVocal,TimeSeries_i_ID,jj);
	end
else
    % Normal FOR Loop (serial)
    if strcmp(howVocal,'minimal')
        BF_ProgressBar('new');
    end
	for jj = 1:numMopsToCalc
		[MasterOutput_tmp{jj},MasterCalcTime_tmp(jj)] = ...
			TS_ComputeMasterLoop(x,x_z,par_MasterOpCodeCalc{jj}, ...
				par_mop_ids(jj),numMopsToCalc,howVocal,TimeSeries_i_ID,jj);

        if strcmp(howVocal,'minimal')
            BF_ProgressBar(jj/numMopsToCalc);
        end
	end
    if strcmp(howVocal,'minimal')
        BF_ProgressBar('close');
    end
end

% Map from temporary versions to the full versions:
MasterOutput(Master_ind_calc) = MasterOutput_tmp;
MasterCalcTime(Master_ind_calc) = MasterCalcTime_tmp;

if strcmp(howVocal,'full')
    fprintf(1,'%u time-series features evaluated in %s///\n\n',...
                        numMopsToCalc,BF_TheTime(toc(masterTimer)));
end
clear('masterTimer')

% --------------------------------------------------------------------------
%% Assign all the results to the corresponding operations
% --------------------------------------------------------------------------
% Set sliced version of matching indicies across the range toCalc
% Indices of MasterOperations corresponding to each Operation (i.e., each index of toCalc)
MasterOp_ind = arrayfun(@(x)find(MasterOperations.ID==x,1),Operations.MasterID);

for jj = 1:numCalc
	try
		[featureVector(jj),calcQuality(jj),calcTimes(jj)] = ...
			TS_ComputeOpLoop(MasterOutput{MasterOp_ind(jj)}, ...
				MasterCalcTime(MasterOp_ind(jj)), ...
				MasterOperations.Label{MasterOp_ind(jj)}, ... % Master label
				Operations.CodeString{jj}); % Code string for each operation to calculate (i.e., in toCalc)
	catch
		fprintf(1,'---Error with %s\n',Operations.CodeString{jj});
		if (MasterOperations.ID(MasterOp_ind(jj)) == 0)
			error(['The operations database is corrupt: there is no link ' ...
					'from ''%s'' to a master code'],Operations.CodeString{jj});
		else
			fprintf(1,'Error retrieving element %s from %s.\n', ...
				Operations.CodeString{jj},MasterOperations.Label{MasterOp_ind(jj)});
		end
	end
end

% --------------------------------------------------------------------------
%% Code special values:
% --------------------------------------------------------------------------
% (*) Errorless calculation: calcQuality = 0, featureVector = <real number>

% (*) Output = NaN: calcQuality = 2, set featureVector = 0
RR = isnan(featureVector); % NaN
if any(RR)
	calcQuality(RR) = 2;
	if codeSpecial
		featureVector(RR) = 0;
	end
end

% (*) Output = Inf: calcQuality = 3, set featureVector = 0
RR = (isinf(featureVector) & featureVector > 0); % Inf
if any(RR)
	calcQuality(RR) = 3;
	if codeSpecial
		featureVector(RR) = 0;
	end
end

% (*) Output = -Inf: calcQuality = 4, set featureVector = 0
RR = (isinf(featureVector) & featureVector < 0);
if any(RR)
	calcQuality(RR) = 4;
	if codeSpecial
		featureVector(RR) = 0;
	end
end

% (*) Output is a complex number: calcQuality = 5, set featureVector = 0
RR = (imag(featureVector)~=0);
if any(RR)
	calcQuality(RR) = 5;
	if codeSpecial
		featureVector(RR) = 0;
	end
end

% (*) Fatal error: calcQuality = 1, featureVector = 0
% 		(this is done already in the code above)
if ~codeSpecial
	% If you want special values in your feature vector (~codeSpecial), then
	% we need to put errors back into the feature vector as NaNs:
	RR = (calcQuality==1);
	featureVector(RR) = NaN;
end

% --------------------------------------------------------------------------
%% Calculation complete: display information about this time series calculation
% --------------------------------------------------------------------------

% Calculate statistics for writing to file/screen
% The number of calculated operations that returned real outputs without errors, numGood:
numGood = sum(calcQuality == 0);
% The number of errors encountered, numErrors:
numErrors = sum(calcQuality == 1);
% The number of other special outputs, numSpecial:
numSpecial = sum(calcQuality > 1);

switch howVocal
case 'full'
    fprintf(1,'********************************************************************\n');
    fprintf(1,'; ; ; : : : : ; ; ; ;   %s    ; ; ; ; : : : ; ; ;\n',datestr(now));
    fprintf(1,'Calculation complete for %s (ts_id = %u, N = %u)\n', ...
                        tsStruct.Name,tsStruct.ID,tsStruct.Length);
    fprintf(1,'%u real-valued outputs, %u errors, %u special-valued outputs stored.\n',...
                        numGood,numErrors,numSpecial);
    fprintf(1,'All %u calculations for this time series took %s.\n',numCalc,BF_TheTime(toc(fullTimer),1));
    fprintf(1,'********************************************************************\n');
case 'minimal'
    fprintf(1,'Calculation complete for %s (ts_id = %u, N = %u) in %s\n', ...
                        tsStruct.Name,tsStruct.ID,tsStruct.Length,BF_TheTime(toc(fullTimer),1));
    fprintf(1,'%u real-valued outputs, %u errors, %u special-valued outputs stored.\n\n',...
                        numGood,numErrors,numSpecial);
case 'fast'
    % (none)
end

end


function r = exprand(mu,m,n)
% EXPRAND Random matrices from exponential distribution.
%
%    R = EXPRAND(MU) returns a matrix of random numbers chosen
%    from the exponential distribution with parameter MU.
%    The size of R is the size of MU.
%    Alternatively, R = EXPRAND(MU,M,N) returns an M by N matrix.
%
% Copyright (c) 1998-2004 Aki Vehtari

% This software is distributed under the GNU General Public
% License (version 3 or later); please refer to the file
% License.txt, included with the software, for details.

error('No mex-file for this architecture. See Matlab help and convert.m in ./linuxCsource or ./winCsource for help.')


function [inputMovie] = downsampleMovie(inputMovie, varargin)
	% Downsamples a movie in either space or time, uses floor to calculate downsampled dimensions.
	% Biafra Ahanonu
	% started 2013.11.09 [09:31:32]
	%
	% inputs
		% inputMovie: a NxMxP matrix
	% options
		% downsampleType
		% downsampleFactor - amount to downsample in time

	[inputMovie] = ciapkg.movie_processing.downsampleMovie(inputMovie,'passArgs', varargin);
end


function CPDpot = convert_dbn_CPDs_to_tables1(bnet, evidence)
% CONVERT_DBN_CPDS_TO_TABLES Convert CPDs of (possibly instantiated) DBN nodes to tables
% CPDpot = convert_dbn_CPDs_to_tables(bnet, evidence)
%
% CPDpot{n,t} is a table containing P(n,t|pa(n,t), ev)
% All hidden nodes are assumed to be discrete
% We assume the observed nodes are the same in every slice
%
% Evaluating the conditional likelihood of the evidence can be very slow,
% so we take pains to vectorize where possible, i.e., we try to avoid
% calling convert_to_table

[ss T] = size(evidence);
%obs_bitv = ~isemptycell(evidence(:));
obs_bitv = zeros(1, 2*ss);
obs_bitv(bnet.observed) = 1;
obs_bitv(bnet.observed+ss) = 1;

ns = bnet.node_sizes(:);
CPDpot = cell(ss,T);

for n=1:ss
  % slice 1
  t = 1;
  ps = parents(bnet.dag, n);
  e = bnet.equiv_class(n, 1);
  if ~any(obs_bitv(ps))
    CPDpot{n,t} = convert_CPD_to_table_hidden_ps(bnet.CPD{e}, evidence{n,t});
  else
    CPDpot{n,t} = convert_to_table(bnet.CPD{e}, [ps n], evidence(:,1));
  end

  % slices 2..T
  debug = 1;
  if ~obs_bitv(n)
    CPDpot = helper_hidden_child(bnet, evidence, n, CPDpot, obs_bitv, debug);
  else
    CPDpot = helper_obs_child(bnet, evidence, n, CPDpot, obs_bitv, debug);
  end
end

if 0
CPDpot2 = convert_dbn_CPDs_to_tables_slow(bnet, evidence);
for t=1:T
  for n=1:ss
    if ~approxeq(CPDpot{n,t}, CPDpot2{n,t})
      fprintf('CPDpot n=%d, t=%d\n',n,t);
      keyboard
    end
  end
end
end

% special cases: c=child, p=parents, d=discrete, h=hidden, 1=1slice
% if c=h=1 then c=d=1, since hidden nodes must be discrete
% c=h c=d p=h p=d p=1 method
% ---------------------------
% 1   1   1   1   -   replicate CPT
% 0   1   1   1   1   dhmm
% 0   0   1   1   1   ghmm
% -   1   -   1   -   evaluate CPT on evidence
% other               loop

%%%%%%%
function CPDpot = helper_hidden_child(bnet, evidence, n, CPDpot, obs_bitv, debug)

[ss T] = size(evidence);
self = n+ss;
ps = parents(bnet.dag, self);
e = bnet.equiv_class(n, 2);
ns = bnet.node_sizes(:);
if ~any(obs_bitv(ps)) % all parents are hidden (hence discrete)
  if debug, fprintf('node %d is hidden, all ps are hidden\n', n); end
  if myismember(n, bnet.dnodes)
    %CPT = CPD_to_CPT(bnet.CPD{e});
    %CPT = reshape(CPT, [prod(ns(ps)) ns(self)]);
    CPT = convert_CPD_to_table_hidden_ps(bnet.CPD{e}, []);
    CPDpot(n,2:T) = num2cell(repmat(CPT, [1 1 T-1]), [1 2]);
  else
    error(['hidden cts node disallowed'])
  end
else % some parents are observed - slow
  if mysubset(ps, bnet.dnodes) % all parents are discrete
    % given CPT(p1, p2, p3, p4, c), where p1,p3 are observed
    % we create CPT([p2 p4 c], [p1 p3]).
    % We then convert all observed p1,p3 into indices ndx
    % and return CPT(:, ndx)
    CPT = CPD_to_CPT(bnet.CPD{e});
    domain = [ps self];
    % if dom is [3 7 8] and 3,8 are observed, odom_rel = [1 3], hdom_rel = 2,
    % odom = [3 8], hdom = 7
    odom_rel = find(obs_bitv(domain));
    hdom_rel = find(~obs_bitv(domain));
    odom = domain(odom_rel);
    hdom = domain(hdom_rel);
    CPT = permute(CPT, [hdom_rel odom_rel]);
    CPT = reshape(CPT, prod(ns(hdom)), prod(ns(odom)));
    parents_in_same_slice = all(ps > ss);
    if parents_in_same_slice
      if debug, fprintf('node %d is hidden, some ps are obs, all ps discrete, 1 slice\n', n); end
      data = cell2num(evidence(odom-ss,2:T)); %data(i,t) = val of i'th obs parent at t+1
    else
      if debug, fprintf('node %d is hidden, some ps are obs, all ps discrete, 2 slice\n', n); end
      data = zeros(length(odom), T-1);
      for t=2:T
	ev = evidence(:,t-1:t);
	data(:,t-1) = cell2num(ev(odom));
      end
    end
    ndx = subv2ind(ns(odom), data'); % ndx(t) encodes data(:,t)
    CPDpot(n,2:T) = num2cell(CPT(:, ndx), [1 2]);
  else % some parents are cts - v slow
    if debug, fprintf('node %d is hidden, some ps are obs, some ps cts\n', n); end
    for t=2:T
      CPDpot{n,t} = convert_to_table(bnet.CPD{e}, [ps self], evidence(:,t-1:t));
    end
  end
end

%%%%%%%
function CPDpot = helper_obs_child(bnet, evidence, n, CPDpot, obs_bitv, debug)

[ss T] = size(evidence);
self = n+ss;
ps = parents(bnet.dag, self);
e = bnet.equiv_class(n, 2);
ns = bnet.node_sizes(:);
if ~any(obs_bitv(ps)) % all parents are hidden
  parents_in_same_slice = all(ps > ss);
  if parents_in_same_slice
    if debug, fprintf('node %d is obs, all ps are hidden, 1 slice\n', n); end
    ps1 = ps - ss;
    if myismember(n, bnet.dnodes)
      CPT = CPD_to_CPT(bnet.CPD{e});
      CPT = reshape(CPT, [prod(ns(ps)) ns(self)]); % what if no parents?
      obslik = eval_pdf_cond_multinomial(cell2num(evidence(n,2:T)), CPT);
      CPDpot(n,2:T) = num2cell(obslik, 1);
    else
      S = struct(bnet.CPD{e});
      obslik = eval_pdf_cond_gauss(cell2num(evidence(n,2:T)), S.mean, S.cov);
      CPDpot(n,2:T) = num2cell(obslik, 1);
    end
  else % parents span 2 slices - slow
    if debug, fprintf('node %d is obs, all ps are hidden , 2 slice\n', n); end
    for t=2:T
      CPDpot{n,t} = convert_to_table(bnet.CPD{e}, [ps self], evidence(:,t-1:t));
    end
  end
else
  if isempty(ps) % observed root
    if debug, fprintf('node %d is obs, no ps\n', n); end
    CPT = CPD_to_CPT(bnet.CPD{e});
    data = cell2num(evidence(n,2:T));
    CPDpot(n,2:T) = CPT(data);
  else  % some parents are observed  - slow
    if debug, fprintf('node %d is obs, some ps are obs\n', n); end
    for t=2:T
      CPDpot{n,t} = convert_to_table(bnet.CPD{e}, [ps self], evidence(:,t-1:t));
    end
  end
end


function eventLog = ma_executeDVManeuver_dv_inertial_spherical(dvVectInertialSpherical, thruster, initialState, eventNum)
%ma_executeDVManeuver_dv_inertial_spherical Summary of this function goes here
%   Detailed explanation goes here

    azimuth = dvVectInertialSpherical(1);
    elevation = dvVectInertialSpherical(2);
    r = dvVectInertialSpherical(3);

    [x,y,z] = sph2cart(azimuth,elevation,r);
    dvVectInertial = [x,y,z];

    eventLog = ma_executeDVManeuver_dv_inertial(dvVectInertial, thruster, initialState, eventNum);
end