function [feature_pos_out, transformed_points_out, A_out, error] = em2_affine(...
    events, ...
    feature_pos, ...
    template_points, ...
    template_weights, ...
    A_in, ...
    flow, ...
    params, ...
    fig)
%EM2_AFFINE Estimates an affine transform between a set of events and a set
%of template points.
%
% EM2_AFFINE estimates an affine warp (A, b) between a set of events with
% known optical flow, and a set of 2D template points. This EM step runs
% from the events alone, as in:
% Alex Zihao Zhu, Nikolay Atanasov and Kostas Daniilidis.
% "Event-based Feature Tracking with Probabilistic Data Association",
% IEEE International Conference on Robotics and Automation (ICRA), 2017.
%
% Syntax:  [feature_pos_out, transformed_points_out, A_out, error] = EM2_AFFINE(...
%            events, ...
%            feature_pos, ...
%            template_points, ...
%            template_weights, ...
%            A_in, ...
%            flow, ...
%            params, ...
%            fig)
%
% Inputs:
%    events           - 4xN, each column is (x,y,t,p).
%    feature_pos      - 2x1, pixel position of the feature.
%    template_points  - 2xM, set of points to align the events with.
%    template_weights - 1xM, weights for each template point.
%    A_in             - 2x2, initialization for the affine warp.
%    flow_init        - 2x1, flow for the event window.
%    params           - parameters, defined in get_params().
%    fig              - figure handle for plotting.
%
% Outputs:
%    feature_pos_out        - 2x1, feature pos after alignment, feature_pos - b.
%    transformed_points_out - 2xN, input events in the feature window
%                               shifted by the flow A[x; y] + dt * flow + b.
%    A_out                  - 2x2, estimate for the affine warp.
%    error                  - Error code, defined in TrackingErrors.
%
% See also GET_PARAMS
%
% Author: Alex Zihao Zhu, University of Pennsylvania
% Email: alexzhu(at)seas.upenn.edu
% Copyright 2018 University of Pennsylvania
% Alex Zihao Zhu, Nikolay Atanasov, Kostas Daniilidis
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS, CONTRIBUTORS, AND THE
% TRUSTEES OF THE UNIVERSITY OF PENNSYLVANIA "AS IS" AND ANY EXPRESS OR
% IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
% OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
% IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE TRUSTEES OF
% THE UNIVERSITY OF PENNSYLVANIA BE LIABLE FOR ANY DIRECT, INDIRECT,
% INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
% NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
% DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
% THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
% (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
% THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

%% Initialization
% Estimated canonical events
transformed_points_out = [];
% New estimated patch position if estimated
feature_pos_out = [nan; nan];
errs = zeros(params.em2_params.max_iters, 1);
num_iter = 0;
error = TrackingErrors.ICPFailed;
scatter_plot_handle = [];

target_time = events(3, 1);
A = A_in;
A_out = A;
pos_shift = [0;0];

event_window = [];

time_shifted_points = bsxfun(@minus, events(1:2, :), feature_pos) + ...
    bsxfun(@times, flow, (target_time - events(3,:)));

if isempty(template_points)
    return
end

kdtree = KDTreeSearcher(...
    template_points' / (sqrt(2) * params.em2_params.sigma), ...
    'Distance', 'euclidean');

while true
    if num_iter > params.em2_params.max_iters
        error = TrackingErrors.ICPMaxIters;
        return
    end

    transformed_points = bsxfun(@plus, A * time_shifted_points, pos_shift);

    if isempty(event_window)
        event_window = ...
            transformed_points(1, :) >= -params.window_size/2 & ...
            transformed_points(2, :) >= -params.window_size/2 & ...
            transformed_points(1, :) <= params.window_size/2 & ...
            transformed_points(2, :) <= params.window_size/2;

        n_in_window = sum(event_window);
        if n_in_window < params.min_events_for_em
            error = TrackingErrors.ICPFailed;
            return
        end

        time_shifted_points = time_shifted_points(:, event_window);
        transformed_points = transformed_points(:, event_window);
        %         if n_in_window > 200
        %             shifted_events = shifted_events(:, 1:200);
        %             transformed_events = transformed_events(:, 1:200);
        %             n_in_window = 200;
        %         end

        weights = zeros(size(template_points, 2), size(transformed_points, 2));
    end

    [neighbors_cell, distances_cell] = rangesearch(...
        kdtree, ...
        transformed_points' / (sqrt(2) * params.em2_params.sigma), ...
        params.em2_params.max_distance);

    distancesstacked = cell2mat(cellfun(...
        @transpose, distances_cell, 'UniformOutput', false))';

    if isempty(distancesstacked)
        error = TrackingErrors.ICPFailed;
        return
    end

    num_neighbors = cellfun('length', neighbors_cell);
    template_correspondences = cell2mat(cellfun(...
        @transpose, neighbors_cell, 'UniformOutput', false))';

    transformed_correspondences = repelem(1:n_in_window, num_neighbors);

    weightsstacked = exp(-distancesstacked);

    valid_inds = sub2indc(transformed_correspondences, template_correspondences, size(weights));

    % It's cheaper to multiply to 0 to reset the weights matrix than to
    % reinitialize it using zeros.
    weights = weights * 0;
    weights(valid_inds) = weightsstacked;

    if ~isempty(template_weights)
        weights = bsxfun(@times, template_weights, weights);
    end

    weight_sum = sum(weights, 1);
    valid_weights = (weight_sum > 0);

    weights = bsxfun(@rdivide, weights, weight_sum + 1e-10);

    weightsstacked = weights(valid_inds);

    %% Simultaneously minimize over affine warp and translation.
    weighted_template_points = template_points*weights;
    weighted_template_points = weighted_template_points(:, valid_weights);

    current_events = time_shifted_points(:, valid_weights);

    D = [current_events(1:2, :); ones(1, size(current_events, 2))];
    X = weighted_template_points;

    transform = X/D;

    A = transform(1:2, 1:2);
    scale = sqrt(abs(det(A)));
    pos_shift = (transform(:, 3));

    if scale < params.em2_params.min_scale
        error = TrackingErrors.ICPFailed;
        return
    end

    %% Calculate errors, plot debug information.
    err = weightsstacked*distancesstacked'/ size(current_events, 2);
    errs(num_iter+1) = err;

    if params.debug
        set(0, 'CurrentFigure', fig)
        subplot(2,1,1)
        plot(errs(errs > 0), 'b')
        title('Change in error (convergence criterion)')
        xlim([0 params.em2_params.max_iters])
        subplot(2,1,2)
        if (~isempty(scatter_plot_handle))
            delete(scatter_plot_handle)
        end
        scatter_plot_handle = scatter(transformed_points(1, :), transformed_points(2, :),'r.');
        hold on
        scatter(template_points(1, :), template_points(2, :),template_weights*10,'b.');
        hold off
        axis equal
        axis([-params.window_size/2-5, ...
            params.window_size/2+5, ...
            -params.window_size/2-5, ...
            params.window_size/2+5])
        axis ij
        title('EM 2')

        pause(0.01)
    end

    if num_iter
        derr = err - errs(num_iter);
        if derr > -params.em2_params.min_err && derr <= 0
            break;
        end
    end
    num_iter = num_iter + 1;
end

A_out = A;
% Update the feature position based on the shift.
feature_pos_out = feature_pos - pos_shift;

% Compute the template events to be used in later iterations.
dt = events(3, end)-events(3, 1);
time_shifted_points = bsxfun(@minus, events(1:2, :), ...
    feature_pos + flow * dt) + ...
    bsxfun(@times, flow, (events(3, end) - events(3, :)));

transformed_points = A_out*time_shifted_points;

% Make the window size a bit bigger for the template.
window_size = round(params.window_size * 1.5);

event_window = ...
    transformed_points(1, :) >= -window_size/2 & ...
    transformed_points(2, :) >= -window_size/2 & ...
    transformed_points(1, :) < window_size/2 & ...
    transformed_points(2, :) < window_size/2;

transformed_points_out = transformed_points(:, event_window);

if params.debug
    pause(0.5)
end

% Measure how many transformed points are considered 'outliers' when
% matched against the templates. Outliers are defined as points without any
% template points within 2 pixels. Note that this function still requires
% more work to be properly robust.
[~, distances_cell] = knnsearch(...
    kdtree, ...
    transformed_points_out'/(sqrt(2)*params.em2_params.sigma), ...
    'K', 1);
distances_cell = distances_cell * 2 * params.em2_params.sigma^2;
percent_outliers = sum(distances_cell > 2.0) / length(distances_cell);

if percent_outliers > params.em2_params.max_outlier_thresh
    error = TrackingErrors.ICPError;
    feature_pos_out = [nan; nan];
    return;
end

end


function [tracks_interp,trk_mean_length] = trk_interp(tracks,nPoints_new,spacing,tie_at_center)
%TRK_INTERP - Interpolate tracks with cubic B-splines
%Streamlines will be resampled to have a new number of vertices. May be useful
%to groom your tracks first. Can be multithreaded if Parallel Computing Toolbox
%is installed.
%
% Syntax: [tracks_interp,trk_mean_length] = trk_interp(tracks,nPoints_new,spacing,tie_at_center)
%
% Inputs:
%    tracks      - Struc array output of TRK_READ [1 x nTracks]
%    nPoints_new - Constant # mode: Number of vertices for each streamline
%                  (spacing between vertices will vary between streamlines)
%                  (Default: 100)
%    spacing     - Constant spacing mode: Spacing between each vertex (# of
%                  vertices will vary between streamlines). Note: Only supply
%                  nPoints_new *OR* spacing!
%    tie_at_center - (Optional) Use with nPoints_new to add an additional
%                  "tie-down" point at the midpoint of the tract. Recommended.
%                  http://github.com/johncolby/along-tract-stats/wiki/tie-at-center
%
% Outputs:
%    tracks_interp   - Interpolated tracks in matrix format.
%                      [nPoints_new x 3 x nTracks]
%    trk_mean_length - The length of the mean tract geometry if using the
%                      spacing parameter, above. Useful to then normalize track
%                      lengths between subjects.
%
% Example:
%    exDir                   = '/path/to/along-tract-stats/example';
%    subDir                  = fullfile(exDir, 'subject1');
%    trkPath                 = fullfile(subDir, 'CST_L.trk');
%    volPath                 = fullfile(subDir, 'dti_fa.nii.gz');
%    volume                  = read_avw(volPath);
%    [header tracks]         = trk_read(trkPath);
%    tracks_interp           = trk_interp(tracks, 100);
%    tracks_interp           = trk_flip(header, tracks_interp, [97 110 4]);
%    tracks_interp_str       = trk_restruc(tracks_interp);
%    [header_sc tracks_sc]   = trk_add_sc(header, tracks_interp_str, volume, 'FA');
%    [scalar_mean scalar_sd] = trk_mean_sc(header_sc, tracks_sc);
%
% Other m-files required: Curve Fitting Toolbox (aka Spline Toolbox)
% Subfunctions: none
% MAT-files required: none
%
% See also: TRK_READ, SPLINE

% Author: John Colby (johncolby@ucla.edu)
% UCLA Developmental Cognitive Neuroimaging Group (Sowell Lab)
% Apr 2010

if nargin<4, tie_at_center = []; end
if nargin<3, spacing = []; end
if nargin<2 || isempty(nPoints_new), nPoints_new = 100; end

tracks_interp   = zeros(nPoints_new, 3, length(tracks));
trk_mean_length = [];
pp = repmat({[]},1,length(tracks));

% Interpolate streamlines so that each has the same number of vertices, spread
% evenly along its length (i.e. vertex spacing will vary between streamlines)
for iTrk=1:length(tracks)
    tracks_tmp = tracks(iTrk);

    % Martin: bugfix for single point fibers
    if tracks_tmp.nPoints == 1
        tracks_interp(:,:,iTrk) = repmat(tracks_tmp.matrix, nPoints_new, 1);
        continue
    end

    % Determine streamline segment lengths
    segs = sqrt(sum((tracks_tmp.matrix(2:end,1:3) - tracks_tmp.matrix(1:(end-1),1:3)).^2, 2));
    dist = [0; cumsum(segs)];

    % Remove duplicates
    [dist I J]= unique(dist);
    % Martin: bugfix for fibers with same points
    if length(dist) == 1
        dist = 0:1;
        I = 1:2;
    end

    % Fit spline
    % Martin: bugfix for fibers with more than 3 dimensions
    pp{iTrk} = spline(dist, tracks_tmp.matrix(I,1:3)');

    % Resample streamline along the spline
    tracks_interp(:,:,iTrk) = ppval(pp{iTrk}, linspace(0, max(dist), nPoints_new))';
end

% Interpolate streamlines so that the vertices have equal spacing for a central
% "tie-down" origin. This means streamlines will have varying #s of vertices
if ~isempty(spacing)
    % Calculate streamline lengths
    lengths = trk_length(tracks_interp);

    % Determine the mean tract geometry and grab the middle vertex
    track_mean      = mean(tracks_interp, 3);
    trk_mean_length = trk_length(track_mean);
    middle          = track_mean(round(length(track_mean)/2),:);

    % Interpolate streamlines again, but this time sample with constant vertex
    % spacing for all streamlines. This means that the longer streamlines will now
    % have more vertices.
    tracks_interp = repmat(struct('nPoints', 0, 'matrix', [], 'tiePoint', 0), 1, length(tracks));
    for iTrk=1:length(tracks)
        tracks_interp(iTrk).matrix  = ppval(pp{iTrk}, 0:spacing:lengths(iTrk))';
        tracks_interp(iTrk).nPoints = size(tracks_interp(iTrk).matrix, 1);

        % Also determine which vertex is the "tie down" point by finding the one
        % closest to the middle point of the mean tract geometry
        dists = sqrt(sum(bsxfun(@minus, tracks_interp(iTrk).matrix, middle).^2,2));
        [tmp, ind] = min(dists);
        tracks_interp(iTrk).tiePoint = ind;
    end
end

% Streamlines will all have the same # of vertices, but now they will be spread
% out so that an equal proportion lies on either side of a central origin.
if ~isempty(nPoints_new) && ~isempty(tie_at_center)
    % Make nPoints_new odd
    nPoints_new_odd = floor(nPoints_new/2)*2+1;

    % Calculate streamline lengths
    lengths = trk_length(tracks);

    % Determine the mean tract geometry and grab the middle vertex
    track_mean      = mean(tracks_interp, 3);
    trk_mean_length = trk_length(track_mean);
    middle          = track_mean(round(length(track_mean)/2),:);

    tracks_interp_tmp = zeros(nPoints_new_odd, 3, length(tracks));

    for iTrk=1:length(tracks)
        dists = sqrt(sum(bsxfun(@minus, tracks_interp(:,:,iTrk), middle).^2,2));
        [tmp, ind] = min(dists);

        first_half  = ppval(pp{iTrk}, linspace(0, lengths(iTrk)*(ind/nPoints_new), ceil(nPoints_new_odd/2)))';
        second_half = ppval(pp{iTrk}, linspace(lengths(iTrk)*(ind/nPoints_new), lengths(iTrk), ceil(nPoints_new_odd/2)))';
        tracks_interp_tmp(:,:,iTrk) = [first_half; second_half(2:end,:)];
    end

    tracks_interp = tracks_interp_tmp;
end


function update_background_parallel(obj, use_parallel)
%% update the background related variables in CNMF framework
% input:
%   use_parallel: boolean, do initialization in patch mode or not.
%       default(true); we recommend you to set it false only when you want to debug the code.

%% Author: Pengcheng Zhou, Columbia University, 2017
%% email: zhoupc1988@gmail.com

%% process parameters

try
    % map data
    mat_data = obj.P.mat_data;

    % folders and files for saving the results
    log_file =  obj.P.log_file;
    flog = fopen(log_file, 'a');
    log_data = matfile(obj.P.log_data, 'Writable', true); %#ok<NASGU>

    % dimension of data
    dims = mat_data.dims;
    d1 = dims(1);
    d2 = dims(2);
    T = dims(3);
    obj.options.d1 = d1;
    obj.options.d2 = d2;

    % parameters for patching information
    patch_pos = mat_data.patch_pos;
    block_pos = mat_data.block_pos;

    % number of patches
    [nr_patch, nc_patch] = size(patch_pos);
catch
    error('No data file selected');
end
fprintf('\n-----------------UPDATE BACKGROUND---------------------------\n');

% frames to be loaded for initialization
frame_range = obj.frame_range;
T = diff(frame_range) + 1;

% threshold for detecting large residuals
thresh_outlier = obj.options.thresh_outlier;

% use parallel or not
if ~exist('use_parallel', 'var')||isempty(use_parallel)
    use_parallel = true; %don't save initialization procedure
end

% options
options = obj.options;
nb = options.nb;
bg_ssub = options.bg_ssub;
bg_model = options.background_model;
with_projection = options.bg_acceleration;

% previous estimation
A = cell(nr_patch, nc_patch);
C = cell(nr_patch, nc_patch);
sn = cell(nr_patch, nc_patch);
W = obj.W;
b0 = obj.b0;
b = obj.b;
f = obj.f;
RSS = cell(nr_patch, nc_patch);

%% check whether the bg_ssub was changed
if strcmpi(bg_model, 'ring')
    tmp_block = block_pos{1};    % block position
    nr_block = diff(tmp_block(1:2))+1;
    nc_block = diff(tmp_block(3:4))+1;
    [~, temp] = size(W{1});
    [d1s, d2s] = size(imresize(zeros(nr_block, nc_block), 1/bg_ssub));
    if temp~=d1s*d2s
        rr = ceil(obj.options.ring_radius/bg_ssub);    % radius of the ring
        [r_shift, c_shift] = get_nhood(rr, obj.options.num_neighbors);    % shifts used for acquiring the neighboring pixels on the ring
        parfor mpatch=1:(nr_patch*nc_patch)
            tmp_patch = patch_pos{mpatch};    % patch position
            tmp_block = block_pos{mpatch};    % block position
            nr = diff(tmp_patch(1:2)) + 1;
            nc = diff(tmp_patch(3:4)) + 1;
            nr_block = diff(tmp_block(1:2))+1;
            nc_block = diff(tmp_block(3:4))+1;
            b0{mpatch} = zeros(nr*nc, 1);

            if bg_ssub==1
                [csub, rsub] = meshgrid(tmp_patch(3):tmp_patch(4), tmp_patch(1):tmp_patch(2));
                csub = reshape(csub, [], 1);
                rsub = reshape(rsub, [], 1);
                ii = repmat((1:numel(csub))', [1, length(r_shift)]);
                csub = bsxfun(@plus, csub, c_shift);
                rsub = bsxfun(@plus, rsub, r_shift);
                ind = and(and(csub>=1, csub<=d2), and(rsub>=1, rsub<=d1));
                jj = (csub-tmp_block(3)) * (diff(tmp_block(1:2))+1) + (rsub-tmp_block(1)+1);

                temp = sparse(ii(ind), jj(ind), 1, nr*nc, nr_block*nc_block);
                W{mpatch} = bsxfun(@times, temp, 1./sum(temp, 2));
            else
                d1s = ceil(nr_block/bg_ssub);
                d2s = ceil(nc_block/bg_ssub);

                [csub, rsub] = meshgrid(1:d2s, 1:d1s);
                csub = reshape(csub, [], 1);
                rsub = reshape(rsub, [], 1);
                ii = repmat((1:numel(csub))', [1, length(r_shift)]);
                csub = bsxfun(@plus, csub, c_shift);
                rsub = bsxfun(@plus, rsub, r_shift);
                jj = (csub-1) * d1s + rsub;
                % remove neighbors that are out of boundary
                ind = and(and(csub>=1, csub<=d2s), and(rsub>=1, rsub<=d1s));
                temp = sparse(ii(ind), jj(ind), 1, d1s*d2s, d1s*d2s);
                W{mpatch} = bsxfun(@times, temp, 1./sum(temp, 2));
            end
        end
    end
end

%% start updating the background
for mpatch=1:(nr_patch*nc_patch)
    tmp_block = block_pos{mpatch};

    % find the neurons that are within the block
    mask = zeros(d1, d2);
    mask(tmp_block(1):tmp_block(2), tmp_block(3):tmp_block(4)) = 1;

    ind = (reshape(mask(:), 1, [])* obj.A>0);
    A{mpatch}= obj.A(logical(mask), ind);
    C{mpatch} = obj.C(ind, :);
    temp = obj.P.sn(logical(mask));
    if bg_ssub==1
        sn{mpatch} = temp;
    else
        nr_block = diff(tmp_block(1:2))+1;
        nc_block = diff(tmp_block(3:4))+1;
        sn{mpatch} = imresize(reshape(temp, nr_block, nc_block), 1/bg_ssub, 'nearest')*bg_ssub;
    end
end

% check whether this is the first run of updating background components
if strcmpi(bg_model, 'ring')
    flag_first = (length(unique(W{1}(1, :)))==2);
else
    flag_first = (mean2(b{1})==0);
end

if use_parallel
    % load data before running parfor
    %     data_patch = cell(nr_patch, nc_patch);
    %     flag_ignore = cell(nr_patch, nc_patch);
    %     for mpatch=1:(nr_patch*nc_patch)
    %         tmp_patch = patch_pos{mpatch};
    %         A_block = A{mpatch};
    %
    %         % stop updating B because A&C doesn't change in this area
    %         if isempty(A_block) && (~flag_first)
    %             flag_ignore{mpatch} = true;
    %             [r, c] = ind2sub([nr_patch, nc_patch], mpatch);
    %
    %             % keep the current results. this step looks rediculous, but it
    %             % is needed for some computer/matlab. very weird.
    %             W{mpatch} = W{mpatch};
    %             b0{mpatch} = b0{mpatch};
    %             b{mpatch} = b{mpatch};
    %             f{mpatch} = f{mpatch};
    %             fprintf('Patch (%2d, %2d) is done. %2d X %2d patches in total. \n', r, c, nr_patch, nc_patch);
    %             continue;
    %         else
    %             flag_ignore{mpatch} = false;
    %             % pull data
    %             data_patch{mpatch} = get_patch_data(mat_data, tmp_patch, frame_range, true);
    %         end
    %     end
    % do the actual computation
    parfor mpatch=1:(nr_patch*nc_patch)
        %         if flag_ignore{mpatch}
        %             continue;
        %         end
        tmp_patch = patch_pos{mpatch};
        tmp_block = block_pos{mpatch};

        A_block = A{mpatch};
        sn_block = sn{mpatch};
        C_block = C{mpatch};

        % stop updating B because A&C doesn't change in this area
        if isempty(A_block) && (~flag_first)
            [r, c] = ind2sub([nr_patch, nc_patch], mpatch);

            % keep the current results. this step looks rediculous, but it
            % is needed for some computer/matlab. very weird.
            W{mpatch} = W{mpatch};
            b0{mpatch} = b0{mpatch};
            b{mpatch} = b{mpatch};
            f{mpatch} = f{mpatch};
            fprintf('Patch (%2d, %2d) is done. %2d X %2d patches in total. \n', r, c, nr_patch, nc_patch);
            continue;
        end

        % use ind_patch to indicate pixels within the patch and only
        % update (W, b0) corresponding to these pixels
        ind_patch = false(diff(tmp_block(1:2))+1, diff(tmp_block(3:4))+1);
        ind_patch((tmp_patch(1):tmp_patch(2))-tmp_block(1)+1, (tmp_patch(3):tmp_patch(4))-tmp_block(3)+1) = true;

        % pull data
        %         Ypatch = data_patch{mpatch};
        Ypatch = get_patch_data(mat_data, tmp_patch, frame_range, true);
        [nr_block, nc_block, T_block] = size(Ypatch);
        if strcmpi(bg_model, 'ring')
            % get the previous estimation
            W_old = W{mpatch};
            Ypatch = reshape(Ypatch, [], T);

            % run regression to get A, C, and W, b0
            if bg_ssub==1
                sn_patch = sn_block(ind_patch);
                [W{mpatch}, b0{mpatch}] = fit_ring_model(Ypatch, A_block, C_block, W_old, thresh_outlier, sn_patch, ind_patch, with_projection);
            else
                % downsapmle data first
                temp = reshape(double(Ypatch)-A_block*C_block, nr_block, nc_block, T_block);
                tmp_b0 = mean(temp, 3);
                b0{mpatch} = tmp_b0(ind_patch);
                Ypatch = imresize(temp, 1./bg_ssub, 'nearest');
                Ypatch = reshape(Ypatch, [], T_block);

                [W{mpatch}, ~] = fit_ring_model(Ypatch, [], [], W_old, thresh_outlier, sn_block(:), [],  with_projection);
                %                 tmp_b0 = imresize(reshape(tmp_b0, size(sn_block)), [nr_block, nc_block]);
                %                 b0{mpatch} = tmp_b0(ind_patch(:));
            end
        elseif strcmpi(bg_model, 'nmf')
            b_old = b{mpatch};
            f_old = f{mpatch};
            Ypatch = reshape(Ypatch, [], T);
            sn_patch = sn_block(ind_patch);
            [b{mpatch}, f{mpatch}] = fit_nmf_model(Ypatch, nb, A_block, C_block, b_old, f_old, thresh_outlier, sn_patch, ind_patch);
        else
            b_old = b{mpatch};
            f_old = f{mpatch};
            Ypatch = reshape(Ypatch, [], T);
            sn_patch = sn_block(ind_patch);
            [b{mpatch}, f{mpatch}, b0{mpatch}] = fit_svd_model(Ypatch, nb, A_block, C_block, b_old, f_old, thresh_outlier, sn_patch, ind_patch);
        end
        [r, c] = ind2sub([nr_patch, nc_patch], mpatch);
        fprintf('Patch (%2d, %2d) is done. %2d X %2d patches in total. \n', r, c, nr_patch, nc_patch);

    end
else
    for mpatch=1:(nr_patch*nc_patch)
        tmp_patch = patch_pos{mpatch};
        tmp_block = block_pos{mpatch};

        A_block = A{mpatch};
        sn_block = sn{mpatch};
        C_block = C{mpatch};

        % stop the updating B because A&C doesn't change in this area
        if isempty(A_block) && (~flag_first)
            [r, c] = ind2sub([nr_patch, nc_patch], mpatch);
            fprintf('Patch (%2d, %2d) is done. %2d X %2d patches in total. \n', r, c, nr_patch, nc_patch);
            continue;
        end

        % use ind_patch to indicate pixels within the patch and only
        % update (W, b0) corresponding to these pixels
        ind_patch = false(diff(tmp_block(1:2))+1, diff(tmp_block(3:4))+1);
        ind_patch((tmp_patch(1):tmp_patch(2))-tmp_block(1)+1, (tmp_patch(3):tmp_patch(4))-tmp_block(3)+1) = true;

        % pull data
        Ypatch = get_patch_data(mat_data, tmp_patch, frame_range, true);
        [nr_block, nc_block, T_block] = size(Ypatch);
        if strcmpi(bg_model, 'ring')
            % get the previous estimation
            W_old = W{mpatch};
            Ypatch = reshape(Ypatch, [], T_block);

            % run regression to get A, C, and W, b0
            if bg_ssub==1
                sn_patch = sn_block(ind_patch);
                [W{mpatch}, b0{mpatch}] = fit_ring_model(Ypatch, A_block, C_block, W_old, thresh_outlier, sn_patch, ind_patch, with_projection);
            else
                % downsapmle data first
                temp = reshape(double(Ypatch)-A_block*C_block, nr_block, nc_block, T_block);
                tmp_b0 = mean(temp, 3);
                b0{mpatch} = tmp_b0(ind_patch);
                Ypatch = imresize(temp, 1./bg_ssub, 'nearest');
                Ypatch = reshape(Ypatch, [], T_block);

                [W{mpatch}, ~] = fit_ring_model(Ypatch, [], [], W_old, thresh_outlier, sn_block(:), [],  with_projection);
                %                 tmp_b0 = imresize(reshape(tmp_b0, size(sn_block)), [nr_block, nc_block]);
                %                 b0{mpatch} = tmp_b0(ind_patch(:));
            end
        elseif strcmpi(bg_model, 'nmf')
            b_old = b{mpatch};
            f_old = f{mpatch};
            Ypatch = reshape(Ypatch, [], T);
            sn_patch = sn_block(ind_patch);
            [b{mpatch}, f{mpatch}] = fit_nmf_model(Ypatch, nb, A_block, C_block, b_old, f_old, thresh_outlier, sn_patch, ind_patch);
        else
            b_old = b{mpatch};
            f_old = f{mpatch};
            Ypatch = reshape(Ypatch, [], T);
            sn_patch = sn_block(ind_patch);
            [b{mpatch}, f{mpatch}, b0{mpatch}] = fit_svd_model(Ypatch, nb, A_block, C_block, b_old, f_old, thresh_outlier, sn_patch, ind_patch);
        end
        [r, c] = ind2sub([nr_patch, nc_patch], mpatch);
        fprintf('Patch (%2d, %2d) is done. %2d X %2d patches in total. \n', r, c, nr_patch, nc_patch);

    end
end
obj.b = b;
obj.f = f;
obj.b0 = b0;
obj.W = W;
obj.b0_new = obj.reconstruct_b0();
obj.A_prev = obj.A;
obj.C_prev = obj.C;

%% save the results to log
fprintf('Finished updating background using %s model.\n', bg_model);

fprintf(flog, '[%s]\b', get_minute());
fprintf(flog, 'Finished updating background using %s model.\n', bg_model);
if obj.options.save_intermediate
    bg.b = obj.b;
    bg.f = obj.f;
    bg.b0 = obj.b0;
    bg.W = obj.W;
    tmp_str = get_date();
    tmp_str=strrep(tmp_str, '-', '_');
    eval(sprintf('log_data.bg_%s = bg;', tmp_str));
    fprintf(flog, '\tThe results were saved as intermediate_results.bg_%s\n\n', tmp_str);
end
fclose(flog);


function [MMN] = representativeMolecule(MMN)
% For each moiety, identify a set of representative molecules, based on
% various criteria

[nMoiety,~]=size(MMN.L);

minimalMassMetabolite = cell(nMoiety,1);
fractMinimalMass = zeros(nMoiety,1);
numMinimalMassMetabolites = zeros(nMoiety,1);
for i=1:nMoiety
    bool=(MMN.L(i,:)~=0)';
    minimumMass=min(MMN.mol.Mass(bool));
    bool2 = bool &  MMN.mol.Mass==minimumMass;
    ind = find(bool2);
    %take the first one as a representative minimal Mass
    minimalMassMetabolite{i} = MMN.mol.Mets{ind(1)};
    numMinimalMassMetabolites(i) = length(ind);
    fractMinimalMass(i)=minimumMass/MMN.mol.Mass(ind(1));
end

MMN.moi = addvars(MMN.moi,minimalMassMetabolite,fractMinimalMass,numMinimalMassMetabolites, 'NewVariableNames',{'MinimalMassMol','minimalMassFraction','NumMinimalMassMol'});


function Score = TwoFoldCV(hfig,numK2,M_stim,timelists_names) % need to LoadFullFish first
%% partitions for CV
% timelists = getappdata(hfig,'timelists');
timelists_names = getappdata(hfig,'timelists_names');
% periods = getappdata(hfig,'periods');
% if length(periods)>1,
%     timelistsCV = cell(length(M_stim),2);
%     for k_stim = 1:length(M_stim), % :3
%         i_stim = M_stim(k_stim);
%         TL = timelists{i_stim};
%         period = periods(i_stim);
%         nrep = size(TL,2)/periods(i_stim); % integer
%         n = floor(nrep/2);
%         timelistsCV{k_stim,1} = TL(1):TL(n*period);
%         timelistsCV{k_stim,2} = TL(1+n*period):TL(2*n*period);
%     end
% end

%% CV loop: auto-clustering with the partitions
absIX = getappdata(hfig,'absIX');

Score = zeros(length(M_stim),2);

for k_stim = 1:length(M_stim), % :3
    i_stim = M_stim(k_stim);
    CIX = cell(1,2);
    GIX = cell(1,2);
    for k = 1:2,
        %% Cluster to start auto-clustering
        i_ClusGroup = M_ClusGroup(k_fish);
        i_Cluster = M_Cluster(k_fish);
        ClusGroup = VAR(i_fish).ClusGroup{i_ClusGroup};
        numK = ClusGroup(i_Cluster).numK;
        gIX = ClusGroup(i_Cluster).gIX;
        cIX_abs = ClusGroup(i_Cluster).cIX_abs; % convert absolute index to index used for this dataset
        [~,cIX] = ismember(cIX_abs,absIX);

        % ~UpdateTimeIndex
        tIX = timelistsCV{k_stim,k};
        M_0 = GetTimeIndexedData_Default_Direct(hfig,cIX,tIX,'isAllCells');

        isWkmeans = 1;
        [cIX,gIX] = AutoClustering(cIX,gIX,absIX,i_fish,M_0,isWkmeans,numK2);
        CIX{k} = cIX;
        GIX{k} = gIX;
    end
    % plot cell-matching figure
    Score(k_stim,1) = HungarianCV(CIX{1},CIX{2},GIX{1},GIX{2},timelists_names{i_stim});
    Score(k_stim,2) = HungarianCV(CIX{2},CIX{1},GIX{2},GIX{1},timelists_names{i_stim});
end
end


function [i,P] = spm_voice_i(str)
% Get indices, word strings or priors from lexicon
% FORMAT [str] = spm_voice_i(i)
% FORMAT [i  ] = spm_voice_i(str)
% FORMAT [i,P] = spm_voice_i(str)
%
% str  - string or cell array
% i    - index in lexicon (VOX.LEX)
% P    - corresponding array of prior probabilities

% requires the following in the global variable VOX:
% LEX  - lexical structure array
%
%  This routine returns the index or indices of a word if supplied with a
%  string or cell array. Alternatively, it returns the string corresponding
%  to an index or vector of indices. if called with the second argument, it
%  returns a prior probability matrix where the specified words (for
%  strings) have a prior odds ratio of 64.
%
%  NB: If a string is not in the lexicon, 0 is returned.
%__________________________________________________________________________
% Copyright (C) 2019 Wellcome Trust Centre for Neuroimaging

% Karl Friston
% $Id: spm_voice_i.m 7750 2019-12-05 17:54:29Z spm $

% get timeseries from audio recorder(or from a path
%--------------------------------------------------------------------------

% words in lexicon
%==========================================================================
global VOX
word  = {VOX.LEX.word};                  % words in lexicon

% return cell array of indexed words
%--------------------------------------------------------------------------
if isnumeric(str)
    i = word(str);
    return
end

% return indices
%--------------------------------------------------------------------------
if iscell(str)
    if nargout < 2
        for w = 1:numel(str)
            i(w) = spm_voice_i(str{w});
        end
        return
    else

        % return indices and prior probabilities
        %------------------------------------------------------------------
        nw    = numel(str);
        P     = zeros(numel(VOX.LEX),nw) + 1/64;
        for w = 1:nw
            i      = spm_voice_i(str{w});
            P(i,w) = 1;
        end

        % sum to one constraint
        %------------------------------------------------------------------
        P     = bsxfun(@rdivide,P,sum(P));
        return
    end
end

% get indices
%--------------------------------------------------------------------------
i    = find(strcmp(str,word));
if isempty(i), i = 0; end


%%************************************************************************
%% NTcorr: corrector step for the NT direction.
%%
%% SDPT3: version 3.1
%% Copyright (c) 1997 by
%% K.C. Toh, M.J. Todd, R.H. Tutuncu
%% Last Modified: 16 Sep 2004
%%************************************************************************

function [dX,dy,dZ] = NTcorr(blk,At,par,rp,Rd,sigmu,hRd,...
    dX,dZ,coeff,L,X,Z)

global matfct_options solve_ok

printlevel = par.printlevel;
%%
[rhs,EinvRc] = NTrhsfun(blk,At,par,X,Z,rp,Rd,sigmu,hRd,dX,dZ);
m = length(rp); ncolU = size(coeff.mat12,2);
rhs = [rhs; zeros(m+ncolU-length(rhs),1)];
%%
if strcmp(matfct_options,'chol') || strcmp(matfct_options,'spchol') ...
        || strcmp(matfct_options,'ldl') || strcmp(matfct_options,'spldl')
    [xx,resnrm,solve_ok] = symqmr(coeff,rhs,L,[],[],printlevel);
    if (solve_ok<=0) && (printlevel)
        fprintf('\n  warning: symqmr fails: %3.1f.',solve_ok);
    end
else
    [xx,resnrm,solve_ok] = mybicgstab(coeff,rhs,L,[],[],printlevel);
    if (solve_ok<=0) && (printlevel)
        fprintf('\n  warning: bicgstab fails: %3.1f.',solve_ok);
    end
end
if (printlevel>=3); fprintf('%2.0d ',length(resnrm)-1); end
%%
[dX,dy,dZ] = NTdirfun(blk,At,par,Rd,EinvRc,xx,m);
%%************************************************************************


function o = cat(dr,varargin)
% Concatenate file_array objects.  The result is a non-simple object
% that can no longer be reshaped.
%__________________________________________________________________________
% Copyright (C) 2005-2017 Wellcome Trust Centre for Neuroimaging

%
% $Id: cat.m 7147 2017-08-03 14:07:01Z spm $

if dr>32 || dr<0, error('Unknown command option.'); end
dr   = max(round(dr),1);
d    = ones(nargin-1,16);
tmp  = {};
dpos = 0;
for i=1:nargin-1
    vi = varargin{i};
    if strcmp(class(vi),'file_array')
        sz                = size(vi);
        d(i,1:length(sz)) = sz;
        svi               = struct(vi);
        svi               = svi(:);
        for j=1:length(svi(:))
            if length(svi(j).pos)<dr
                svi(j).pos((length(svi(j).pos)+1):dr) = 1;
            end
            svi(j).pos(dr)= svi(j).pos(dr) + dpos;
        end
        dpos              = dpos + d(i,dr);
        tmp{i}            = svi;
    else
        error(['Conversion to file_array from ' class(vi) ' is not possible.']);
    end
end
if any(diff(d(:,[1:(dr-1) (dr+1):end]),1,1))
    error('All matrices on a row in the bracketed expression must have the same number of rows.');
else
    o = vertcat(tmp{:});
    o = file_array(o);
end


function S = sum(d)
%SUM       Integration functional.
%   This function is deprecated. Use FUNCTIONALBLOCK.SUM.

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

S = linop( functionalBlock.sum(double(d)) );

end


function [x,fval,exitflag,info] = opti_nomad(fun,x0,lb,ub,nlcon,nlrhs,xtype,opts)
%OPTI_NOMAD Solve a Global MINLP / NLP using NOMAD (Nonlinear Optimization using the MADS Algorithm)
%
%   min f(x)       subject to:    nlcon(x) <= nlrhs
%    x                            lb <= x <= ub
%                                 xi in Z
%                                 xj in {0,1}
%
%   x = opti_nomad(fun,x0) solves a Global NLP where fun is the
%   objective function and x0 is a starting guess.
%
%   x = opti_nomad(fun,x0,lb,ub) solves subject to decision variable bounds
%   lb <= x <= ub. Infinite bounds will be ignored by the solver.
%
%   x = opti_nomad(fun,x0,lb,ub,nlcon,nlrhs) solves subject to the
%   nonlinear inequality constraints nlcon(x) <= nlrhs.
%
%   x = opti_nomad(fun,...,nlrhs,xtype) solves subject to integer and
%   binary constraints specified in xtype. xtype is a string of characters
%   ('C', 'I', 'B') for continuous, integer, or binary constraints.
%
%   x = opti_nomad(fun,...,xtype,opts) uses opts to pass optiset options to
%   the solver.
%
%   [x,fval,exitflag,info] = opti_nomad(...) returns the objective value
%   at the solution, together with the solver exitflag, and an information
%   structure.
%
%   THIS IS A WRAPPER FOR NOMAD
%   See supplied Lesser GNU Public License

%   Copyright (C) 2012 Jonathan Currie (I2C2)

if(nargin < 8), opts = optiset; else opts = optiset(opts); end
if(nargin < 7), xtype = []; end
if(nargin < 6), nlrhs = []; end
if(nargin < 5), nlcon = []; end
if(nargin < 4), ub = []; end
if(nargin < 3), lb = []; end
if(nargin < 2), error('NOMAD requires at least 2 arguments'); end

%Addin nomad settings if specified
if(isfield(opts,'solverOpts') && ~isempty(opts.solverOpts))
    nopts = nomadset(opts.solverOpts);
else
    nopts = [];
end
%Override with optiset settings (note NOMAD does not accept unknown (optiset) settings)
nopts = nomadset(nopts,'max_iterations',opts.maxiter,'max_bb_eval',opts.maxfeval,...
                'max_time',opts.maxtime,'display_degree',dispLevel(opts.display),...
                'min_mesh_size',num2str(opts.tolrfun),'iterfun',opts.iterfun);

%If we have any inf bounds, set initial mesh size (C.Tribes idea)
if(any(isinf(lb)) || any(isinf(ub)) && isempty(nopts.initial_mesh_size))
    nopts = nomadset('initial_mesh_size','10'); %better heuristic on size here?
end
%Check we have a valid x0
if(isempty(x0) || any(isnan(x0)))
    error('NOMAD requires an initial guess, x0!');
end

t = tic;
% Run NOMAD
[x, fval, exitflag, iter, nfval] = nomad(fun,x0,lb,ub,nlcon,nlrhs,xtype,nopts);

%Collect Results
info.Iterations = iter;
info.FuncEvals = nfval;
info.Time = toc(t);
info.Algorithm = 'NOMAD: Nonlinear Optimization using the MADS Algorithm';

switch(exitflag)
    case 1
        info.Status = 'Converged / Target Reached';
    case 0
        info.Status = 'Exceeded Iterations / Function Evaluations / Time';
    case -1
        info.Status = 'Infeasible / Mesh Limit Reached';
    case -2
        info.Status = 'Initialization Error';
    case -3
        info.Status = 'NOMAD Error';
    case -5
        info.Status = 'User Exited';
    otherwise
        info.Status = 'NOMAD Error';
end


function report(dir1, suffix1, showcls, do_auc_ap)
% Print scores for all classes.

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

if nargin < 3
  showcls = true;
end

if nargin < 4
  do_auc_ap = false;
end

files = dir([dir1 '/*' suffix1 '.mat']);
classes = cell(length(files), 1);
for i = 1:length(files)
  j = strfind(files(i).name, '_');
  classes{i} = files(i).name(1:j(1)-1);
end

count1 = 0;
for i=1:length(classes)
  cls = classes{i};
  try
    load([dir1 cls suffix1]);
    ap1 = ap;
    if do_auc_ap
      ap2 = xVOCap(recall, prec);
      score2(i) = ap2;
    end
    if showcls
      if do_auc_ap
        fprintf('%12s %.4f\n', cls, ap2);
      else
        fprintf('%12s %.4f\n', cls, ap1);
      end
    else
      if do_auc_ap
        fprintf('%.4f\n', ap2);
      else
        fprintf('%.4f\n', ap1);
      end
    end
    score1(i) = ap1;
  catch
    score1(i) = nan;
    score2(i) = nan;
    if showcls
      fprintf('%12s -\n', cls);
    else
      fprintf('-\n');
    end
  end
end

a1 = nanmean(score1);
if do_auc_ap
  a2 = nanmean(score2);
end
if showcls
  fprintf('%s\n', repmat('-', [1 12]));
  if do_auc_ap
    fprintf('%12s %.4f\n', 'mAP', a2);
  else
    fprintf('%12s %.4f\n', 'mAP', a1);
  end
else
  if do_auc_ap
    fprintf('%.4f\n', a2);
  else
    fprintf('%.4f\n', a1);
  end
end


function voxel2FNpair = fgFNPairs(coords,node2voxel)
% Compute the (fiber,node) pairs in each of the coords
%
%   voxel2FNpair = fgFNPairs(coords,node2voxel)
%
% Explain me.  Part of the diffusion prediction code.
%
% See also: t_arcuatePlotXXX
%
% (c) Stanford VISTA Team

nFiber = length(node2voxel);

nCoords = size(coords,1);
voxel2FNpair = cell(nCoords,1);

for thisFiber=1:nFiber
    % We need to know both the nodes that pass through a voxel
    lst = (node2voxel{thisFiber}~=0);
    nodes = find(lst);

    % And we need to know which voxels they pass through
    voxelsInFiber = node2voxel{thisFiber}(lst);

    % Then we store for each voxel the (fiber,node) pairs that pass through
    % it.
    for jj=1:length(voxelsInFiber)
        voxel2FNpair{voxelsInFiber(jj)} = ...
            [voxel2FNpair{voxelsInFiber(jj)},[thisFiber,nodes(jj)]];
    end
end

return


function [SuperFiber, fg] = dtiComputeSuperFiberRepresentation(fg, clusterlabels, numNodes, M)
%
% [SuperFiber, fg] = dtiComputeSuperFiberRepresentation(fg, [clusterlabels], [numNodes])
%
% Superfibers are a single curve (fiber) that summarizes all of the fibers
% in a single labeled cluster.  For example, one might compute the super
% fiber associated with the arcuate.
%
% Create SuperFiber representation of all the fibers assigned to one
% cluster. If numNodes is not provided, it is assumed that they all are
% resampled to same number of Nodes as in fg.fibers{1}.
%
%  INPUTS:
%    fg            - fiber group structure
%    clusterLabels - Possible labels of the fiber subgroups
%    numNodes      - Number of samples (nodes) along the fiber
%    M             - 'mean' or 'median' for calculating tract core
%
%  OUTPUTS:
%   The parameters in the returned representation are:
%   SuperFiber.fibermeans: means (for every coordinate, every node)
%   SuperFiber.fibervarcovs: low diag var/cov matrix for coordinates
%                          (every node)): [var(x), cov(x, y),
%                          cov(x, z), var(y), cov(y, z), var(z)]
%   SuperFiber.n is number of fibers included in this cluster.
%   fg - returned containes flipped and resampled fibers that produced the
%        superfiber.
%
% Note: while computing superFiber representation, some fibers (or even
% majority of fibers) may have to have been  flipped to group first and
% last points together. The 'fg' returned containes flipped and resampled
% fibers that produced the superfiber.
%
% Algorithm:
%
% All fibers in the group (e.g., optic tract) are resampled to represent
% equal numbers of nodes. We determine the mean and 3D covariance of the
% voxel positions representing that node. We define the core fiber as the
% set of mean positions of the sample nodes.
%
% In subsequent routines, we determine the weight contributed by each voxel
% to the diffusivity measure using the covariance matrix. Specifically, we
% calculate the Mahalanobis distance of each voxel from the mean. If a
% voxel position is X, the mean position is X0, and the covariance matrix
% of the 3D positions is C, then the Mahalanobis distance, d, is sqrt(delta
% Cinv delta), where X-X0 is delta.
%
% The assgined weight is the inverse of the distance, d. This procedure
% assigns more weight to fibers close to the core of the bundle, and the
% meaning of close is derived from the distribution of the points at each
% node as captured by the covariance matrix.
%
%  WEB RESOURCES:
%   mrvBrowseSVN('dtiComputeSuperFiberRepresentation');
%   http://white.stanford.edu/newlm/index.php/Diffusion_properties_along_tr
%   ajectory
%
%  EXAMPLE USAGE:
%   % Create superfiber
%       fg = mtrImportFibers('fiberGroupName.pdb');
%       numNodes = 30;
%       [SuperFiber, sfg] = dtiComputeSuperFiberRepresentation(fg, [], numNodes);
%       sFg = dtiNewFiberGroup([fg.name '_superFiber'],[0 0 155],[],[],SuperFiber.fibers);
%   % Save the superfiber
%       mtrExportFibers(sFg,sFg.name);
%
%  HISTORY:
%   2008       ER wrote it
%   08/11/2009 ER added checks that all start points and all end points are
%              grouped respectively.
%   09.13.2011 LMP Added comments, example usage, web resources and
%              input/output descriptions

if(notDefined('numNodes'))
    numNodes=length(fg.fibers{1});
    if length(unique(cellfun(@length, fg.fibers)))>1
        % The numNodes should be the same across all fiber groups, or
        % The user should provide the numNodes for each of the fiber groups
        error('Either provide numNodes to resample the fg to, or number of nodes in the existing fg should be same across the fibers');
    end
end
if notDefined('M')
    M='mean';
elseif ~strcmp(M,'mean') && ~strcmp(M,'median')
    error('M must be either mean or median')
end
% If cluster labels is not sent in, then we assign cluster labels all the
% value of one according to the size of fg.fibers.  FG is a fiber group,
% and it consists of a set of cells that define the nodes of fibers.  This
% assignment says that all of the fibers in the fiber group are in the same
% cluster.  That cluster is labeled cluster 1.
if(notDefined('clusterlabels')), clusterlabels = ones(size(fg.fibers));end

%Check that the all the starting points and all the end points are grouped.
%This routine also resamples the fibers to numNodes samples.
fg = dtiReorientFibers(fg, numNodes);

% The super name
SuperFiber.name=[fg.name '_SuperFiber'];

%The number of fibers in the fiber group
nfibers = size(fg.fibers, 1);
% This should be the same numNodes we sent in above
numNodes= size(fg.fibers{1}, 2);

% curves is the 3D representation of each node for each fiber.
curves  = zeros(3, numNodes, nfibers);
for ii=1:nfibers
    curves(:, :, ii) = fg.fibers{ii};
end

% For every cluster, there is a superfiber representation
% The clusters are labeled as numbers, so max(clusterlabels) is the number
% of clusters.
for clust = 1:max(clusterlabels)

    % For this cluster, find the number of fibers.
    SuperFiber.n(clust, 1) = size(find(clusterlabels==clust), 1);

    % For every node in the numNode list, compute the mean and covariance
    % cloud of the nodes in 3-space.
    for node = 1:numNodes

        %Find the mean position of the 3D coordinates at this node, for
        %this cluster.
        if strcmp(M,'mean')
            SuperFiber.fibers{clust, 1}(:, node) = mean(curves(:, node, clusterlabels==clust), 3);
        elseif strcmp(M,'median')
            SuperFiber.fibers{clust, 1}(:, node) = median(curves(:, node, clusterlabels==clust), 3);
        end
        % Find the lower diagonal matrix of the cloud of points at this
        % node.  Say, for cluster 1 we find the 3D values at this node.  We
        % permute the coordinates to because (help, explain why and what it
        % is matched to), we compute the covariance matrix of these 3D
        % points. The function tril zeros the upper diagonal entries.
        % Later, ER stores the lower triangular part of the symmetric)
        % matrix.
        %
        % This is a very complicated representation that is probably
        % unpacked later in other functions.  It would be nice to specify
        % in the header what functions need to be aware of this
        % representation.
        LowDiagonalMatrixThisNode = ...
            tril(cov(permute(curves(:, node, clusterlabels==clust), [3 1 2])));

        if size(find(clusterlabels==clust), 1)==1
            %ONLY ONE FIBER IN THE GROUP
            LowDiagonalMatrixThisNode = zeros(3);
        end

        %These are low diagonal elements of the 3x3 covariance matrix.
        SuperFiber.fibervarcovs{clust, 1}(:, node) = ...
            LowDiagonalMatrixThisNode([1:3 5:6 9]');

    end %node

end %clust

return


% Scipt: view_fast.m
[mCat1 mCat2 fPer1e fPer2e fPer1,  fPer2] = ex_SplitCatalog(a, 1983.33, 1, 1, 1, 1);
[mLMagsig mHMagsig fLZmax fLZmean fLZmin fHZmax fHZmean,  fHZmin] = plot_Magsig(mCat1, mCat2 , fPer1, fPer2, 0.1);


function [sos,info] = sossolve(sos,solver_options)
% SOSSOLVE --- Solve a sum of squares program.
%
% SOSP = sossolve(SOSP)
%
% SOSP is the SOS program to be solved.
%
% Alternatively, SOSP = sossolve(SOSP,SOLVER_OPT) also defines the solver
% and/or the solver-specific options respectively by fields
%
% SOLVER_OPT.solver (name of the solver)
% SOLVER_OPT.params (a structure containing solver-specific parameters)
%
% The default values for solvers is 'SeDuMi' with parameter ALG = 2, which
% uses the xz-linearization in the corrector and parameter tol =1e-9. See
% SeDuMi help files or user manual for more detail.
%
% Using a second output argument such as [SOSP,INFO] = sossolve(SOSP) will
% return in INFO numerous information concerning feasibility  and CPU time
% that is generated by the SDP solver.
%

% This file is part of SOSTOOLS - Sum of Squares Toolbox ver 3.00.
%
% Copyright (C)2002, 2004, 2013  A. Papachristodoulou (1), J. Anderson (1),
%                                G. Valmorbida (1), S. Prajna (2),
%                                P. Seiler (3), P. A. Parrilo (4)
% (1) Department of Engineering Science, University of Oxford, Oxford, U.K.
% (2) Control and Dynamical Systems - California Institute of Technology,
%     Pasadena, CA 91125, USA.
% (3) Aerospace and Engineering Mechanics Department, University of
%     Minnesota, Minneapolis, MN 55455-0153, USA.
% (4) Laboratory for Information and Decision Systems, M.I.T.,
%     Massachusetts, MA 02139-4307
%
% Send bug reports and feedback to: sostools@cds.caltech.edu
%
% This program 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 2 of the License, or
% (at your option) any later version.
%
% This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
%

% 12/25/01 - SP
% 01/05/02 - SP - primal
% 01/07/02 - SP - objective
% aug/13 - JA,GV - CDSP,SDPNAL,SDPA solvers and SOS matrix decomposition

if (nargin==1)
   %Default options from old sossolve
   solver_options.solver = 'sdpt3';
   solver_options.params.tol = 1e-9;
   solver_options.params.alg = 2;
elseif ((nargin==2) & ~isnumeric('solver_options') )%2 arguments given,
    if ~isfield(solver_options,'solver')
       solver_options.solver = 'sdpt3';
    end
    if ~isfield(solver_options,'params')
        solver_options.params.tol = 1e-9;%default values for SeDuMi
        solver_options.params.alg = 2;
    end
end

%whenever nargin>=2 options are overwritten
if (nargin==3)
    error('Current SOSTOOLS version does not support call to sossolve with 3 arguments, see manual.');
end;

if ~isempty(sos.solinfo.x)
    error('The SOS program is already solved.');
end;

% Adding slack variables to inequalities
sos.extravar.idx{1} = sos.var.idx{sos.var.num+1};
% SOS variables
I = [find(strcmp(sos.expr.type,'ineq')), find(strcmp(sos.expr.type,'sparse')), find(strcmp(sos.expr.type,'sparsemultipartite'))];
if ~isempty(I)
    sos = addextrasosvar(sos,I);
end;
% SOS variables type II (restricted on interval)
I = find(strcmp(sos.expr.type,'posint'));
if ~isempty(I)
    sos = addextrasosvar2(sos,I);
end;

% Processing all expressions

Atf = []; bf = [];
for i = 1:sos.expr.num
    Atf = [Atf, sos.expr.At{i}];
    bf = [bf; sos.expr.b{i}];
end;

% Processing all variables
[At,b,K,RR] = processvars(sos,Atf,bf);

% Objective function
c = sparse(size(At,1),1);

%% Added by PAP, for compatibility with MATLAB 6.5
if isempty(sos.objective);
    sos.objective = zeros(size(c(1:sos.var.idx{end}-1)));
end
%% End added stuff

c(1:sos.var.idx{end}-1) = c(1:sos.var.idx{end}-1) + sos.objective;       % 01/07/02
c = RR'*c;

pars = solver_options.params;

if strcmp(lower(solver_options.solver),'sedumi')
    % SeDuMi in action
    disp(['Size: ' num2str(size(At))]);
    disp([' ']);
    [x,y,info] = sedumi(At,b,c,K,pars);

elseif strcmp(lower(solver_options.solver),'sdpt3')
    % SDPT3 in action
    smallblkdim = 50;
    save sostoolsdata_forSDPT3 At b c K smallblkdim;
    [blk,At2,C2,b2] = read_sedumi('sostoolsdata_forSDPT3.mat');
    delete sostoolsdata_forSDPT3.mat;
    [obj,X,y,Z,infoSDPT] = sqlp(blk,At2,C2,b2,pars);
    x = zeros(length(c),1);
    cellidx = 1;
    if K.f ~= 0
        x(1:K.f) = X{1}(:);
        cellidx = 2;
    end;
    if K.s(1) ~= 0
        idxX = 1;
        idx = K.f+1;
        smblkdim = 100;
        deblkidx = find(K.s > smblkdim);
        spblkidx = find(K.s <= smblkdim);
        blknnz = [0 cumsum(K.s.*K.s)];
        for i = deblkidx
            dummy = X{cellidx};
            x(idx+blknnz(i):idx+blknnz(i+1)-1) = dummy(:);
            cellidx = cellidx+1;
        end;
        for i = spblkidx
            dummy = X{cellidx}(idxX:idxX+K.s(i)-1,idxX:idxX+K.s(i)-1);
            x(idx+blknnz(i):idx+blknnz(i+1)-1) = dummy(:);
            idxX = idxX+K.s(i);
        end;
    end;
	info.cpusec = infoSDPT.cputime;
	info.iter = infoSDPT.iter;
	if infoSDPT.termcode == 1
		info.pinf = 1;
	else
		info.pinf = (infoSDPT.pinfeas>0.1);
	end;
	if infoSDPT.termcode == 2
		info.dinf = 1;
	else
		info.dinf = (infoSDPT.dinfeas>0.1);
	end;
	if infoSDPT.termcode<= 0
		info.numerr = infoSDPT.termcode;
	else
		info.numerr = 0;
	end;

elseif strcmp(lower(solver_options.solver),'csdp') %6/6/13 JA CSDP interface
    %CSDP in action
    if exist('solver_options.params')
        pars = solver_options.params;
    else
        pars.objtol = 1e-9;
        pars.printlevel = 1;
    end
    if (isfield(K,'f')) %Convert free vars to non-negative LP vars
            n_free = K.f;
            [A,b,c,K] = convertf(At,b,c,K); %K.f set to zero
            At = A';
    end
    c = full(c);
    [x,y,z,info] = csdp(At,b,c,K,pars);
    c=sparse(c);
    % 7/6/13 JA Remove extra entries from x corresponding to LP vars
    if (isfield(K,'f')) %Convert free vars to non-negative LP vars
           index = [n_free+1:2*n_free];
           x(1:n_free) = x(1:n_free)-x(index);
           x(index) = [];
           At(index,:)=[];
           c(index) = [];
    end
    if info~=0
       info.dinf=1;
       info.pinf=1;
    else
       info.dinf=0;
       info.pinf=0;
    end;

elseif strcmp(lower(solver_options.solver),'sdpnal') %6/11/13 JA SDPNAL interface
    % SDPNAL in action
    smallblkdim = 50;
    save sostoolsdata_forSDPNAL At b c K smallblkdim;
    [blk,At2,C2,b2] = read_sedumi('sostoolsdata_forSDPNAL.mat');
    delete sostoolsdata_forSDPNAL.mat;
    try
        [obj,X,y,Z,info] = sdpnal(blk,At2,C2,b2,pars); %run history not returned;
    catch
        [obj,X,y,Z] = sdpnal(blk,At2,C2,b2,pars); %run history not returned;
        info = [];
    end

    x = zeros(length(c),1);
    cellidx = 1;
    if K.f ~= 0
        x(1:K.f) = X{1}(:);
        cellidx = 2;
    end;
    if K.s(1) ~= 0
        idxX = 1;
        idx = K.f+1;
        smblkdim = 100;
        deblkidx = find(K.s > smblkdim);
        spblkidx = find(K.s <= smblkdim);
        blknnz = [0 cumsum(K.s.*K.s)];
        for i = deblkidx
            dummy = X{cellidx};
            x(idx+blknnz(i):idx+blknnz(i+1)-1) = dummy(:);
            cellidx = cellidx+1;
        end;
        for i = spblkidx
            dummy = X{cellidx}(idxX:idxX+K.s(i)-1,idxX:idxX+K.s(i)-1);
            x(idx+blknnz(i):idx+blknnz(i+1)-1) = dummy(:);
            idxX = idxX+K.s(i);
        end;
    end;

    if ~isempty(info)
        if info.pinfeas>1e-6||info.pinfeas>1e-6
            info.dinf=1;
            info.pinf=1;
        else
            info.dinf=0;
            info.pinf=0;
        end;
    else
        info.dinf=1;
        info.pinf=1;
    end

    elseif strcmp(lower(solver_options.solver),'sdpa')
    % SDPA in action

    disp(['Size: ' num2str(size(At))]);
    disp([' ']);
    [x,y,info]=sedumiwrap(At',b,c,K,[],pars);

    info.primalObj
    info.primalError

    info.dualObj
    info.dualError

    if info~=0
       info.dinf=1;
       info.pinf=1;
    else
       info.dinf=0;
       info.pinf=0;
    end;

end;
disp([' ']);
disp(['Residual norm: ' num2str(norm(At'*x-b))]);
disp([' ']);
sos.solinfo.x = x;
sos.solinfo.y = y;
sos.solinfo.RRx = RR*x;
sos.solinfo.RRy = RR*(c-At*y);    % inv(RR') = RR
sos.solinfo.info = info;
sos.solinfo.solverOptions = solver_options;
disp(info)

%return;

% Constructing the (primal and dual) solution vectors and matrices
% If you want to have them, comment/delete the return command above.
% In the future version, these primal and dual solutions will be computed only
% when they are needed. We don't want to store redundant info.

for i = 1:sos.var.num
    switch sos.var.type{i}
    case 'poly'
        sos.solinfo.var.primal{i} = sos.solinfo.RRx(sos.var.idx{i}:sos.var.idx{i+1}-1);
        sos.solinfo.var.dual{i} = sos.solinfo.RRy(sos.var.idx{i}:sos.var.idx{i+1}-1);
    case 'sos'
        primaltemp = sos.solinfo.RRx(sos.var.idx{i}:sos.var.idx{i+1}-1);
        dualtemp = sos.solinfo.RRy(sos.var.idx{i}:sos.var.idx{i+1}-1);
        sos.solinfo.var.primal{i} = reshape(primaltemp,sqrt(length(primaltemp)),sqrt(length(primaltemp)));
        sos.solinfo.var.dual{i} = reshape(dualtemp,sqrt(length(dualtemp)),sqrt(length(dualtemp)));
    end;
end;

for i = 1:sos.extravar.num
    primaltemp = sos.solinfo.RRx(sos.extravar.idx{i}:sos.extravar.idx{i+1}-1);
    dualtemp = sos.solinfo.RRy(sos.extravar.idx{i}:sos.extravar.idx{i+1}-1);
    sos.solinfo.extravar.primal{i} = reshape(primaltemp,sqrt(length(primaltemp)),sqrt(length(primaltemp)));
    sos.solinfo.extravar.dual{i} = reshape(dualtemp,sqrt(length(dualtemp)),sqrt(length(dualtemp)));
end;

sos.solinfo.decvar.primal = sos.solinfo.RRx(1:sos.var.idx{1}-1);
sos.solinfo.decvar.dual = sos.solinfo.RRy(1:sos.var.idx{1}-1);

% ====================================================================================
function sos = addextrasosvar(sos,I)
% Adding slack SOS variables to inequalities

for i = I

    numstates = size(sos.expr.Z{i},2);%GV&JA 6/12/2013

    % Creating extra variable
    maxdeg = full(max(sum(sos.expr.Z{i},2)));
    mindeg = full(min(sum(sos.expr.Z{i},2)));
    Z = monomials(numstates,[floor(mindeg/2):ceil(maxdeg/2)]);
    %disp(['Original : ',num2str(size(Z,1))]);

    % Discarding unnecessary monomials
    maxdegree = max(sos.expr.Z{i},[],1)/2;
    mindegree = min(sos.expr.Z{i},[],1)/2;
    j = 1;
    while (j <= size(Z,1))
        Zdummy1 = maxdegree-Z(j,:);
        Zdummy2 = Z(j,:)-mindegree;
        idx = find([Zdummy1, Zdummy2]<0);
        if ~isempty(idx)
            Z = [Z(1:j-1,:); Z(j+1:end,:)];
        else
            j = j+1;
        end;
    end;
    %disp(['Optimized : ',num2str(size(Z,1))]);

    % Convex hull algorithm
    if strcmp(sos.expr.type{i},'sparse')
        Z2 = sos.expr.Z{i}/2;
        Z = inconvhull(full(Z),full(Z2));
        Z = makesparse(Z);
        %disp(['Optimized again : ',num2str(size(Z,1))]);
    end;

    if strcmp(sos.expr.type{i},'sparsemultipartite')
        Z2 = sos.expr.Z{i}/2;
        info2 = sos.expr.multipart{i};%the vectors of independent variables
        sizeinfo2m = length(info2);
        vecindex = [];
        for indm = 1:sizeinfo2m%for each set of independent variables
            sizeinfo2n(indm) = length(info2{indm});
            for indn = 1:sizeinfo2n(indm)
                if isfield(sos,'symvartable')%
                    varcheckindex = find(info2{indm}(indn)==sos.symvartable);
                    if ~isempty(varcheckindex)
                        vecindex{indm}(indn) = varcheckindex;
                    else
                        vecindex{indm}(indn) = length(info2{1})+find(info2{indm}(indn)==sos.varmat.symvartable);%GV&JA 6/12/2013
                    end

                else
                    % PJS 9/12/13: Update code to handle polynomial objects
                    var = info2{indm}(indn);
                    cvartable = char(sos.varmat.vartable);

                    if ispvar(var)
                        % Convert to string representation
                        var = var.varname;
                    end
                    varcheckindex = find(strcmp(var,sos.vartable));
                    if ~isempty(varcheckindex)
                        vecindex{indm}(indn) = varcheckindex;
                    else
                        vecindex{indm}(indn) = length(info2{1}) + find(strcmp(var,cvartable));
                    end

                    % PJS 9/12/13: Original Code to handle polynomial objects
                    %vecindex{indm}(indn) = find(strcmp(info2{indm}(indn).varname,sos.vartable));
                end;
            end
        end
        Z = sparsemultipart(full(Z),full(Z2),vecindex);
        Z = makesparse(Z);
    end;

    dimp = size(sos.expr.b{i},2);

    % Adding slack variables
    sos.extravar.num = sos.extravar.num + 1;
    var = sos.extravar.num;
    sos.extravar.Z{var} = makesparse(Z);
    [T,ZZ] = getconstraint(Z);
    sos.extravar.ZZ{var} = ZZ;
    sos.extravar.T{var} = T';
    %sos.extravar.idx{var+1} = sos.extravar.idx{var}+size(Z,1)^2;%GVcomment the next slack variable starts in the column i+dim(Z)^2 - the elements of the vectorized square matrix

    sos.extravar.idx{var+1} = sos.extravar.idx{var}+(size(Z,1)*dimp)^2;
    for j = 1:sos.expr.num
        sos.expr.At{j} = [sos.expr.At{j}; ...
                sparse(size(sos.extravar.T{var},1)*dimp^2,size(sos.expr.At{j},2))];
    end

    ZZ = flipud(ZZ);
    T = flipud(T);

    Zcheck = sos.expr.Z{i};
    %this is for the matrix case

    if dimp==1
        % JFS 6/3/2003: Ensure correct size:
        pc.Z = sos.extravar.ZZ{var};
        pc.F = -speye(size(pc.Z,1));
        [R1,R2,newZ] = findcommonZ(sos.expr.Z{i},pc.Z);
        % JFS 6/3/2003: Ensure correct size:

        if isempty(sos.expr.At{i})
            sos.expr.At{i} = sparse(size(sos.expr.At{i},1),size(R1,1));
        end
        %------------
        sos.expr.At{i} = sos.expr.At{i}*R1;
        lidx = sos.extravar.idx{var};
        uidx = sos.extravar.idx{var+1}-1;
        sos.expr.At{i}(lidx:uidx,:) = sos.expr.At{i}(lidx:uidx,:) - sos.extravar.T{var}*pc.F*R2;
        sos.expr.b{i} = R1'*sos.expr.b{i};
        sos.expr.Z{i} = newZ;

    else

        [R1,R2,Znew] = findcommonZ(Zcheck,ZZ);

        R1 = fliplr(R1);
        R2 = fliplr(R2);
        Znew = flipud(Znew);

        R1sum = sum(R1,1);
        T = R2'*T;

        ii = 1;
        sig_ZZ = size(ZZ,1);
        sig_Z = size(Z,1);
        sig_Znew = size(Znew,1);

        Tf = sparse(dimp^2*sig_Znew,(dimp*sig_Z)^2);
        Sv = sparse(sig_Znew*dimp^2,1);
        for j = 1:sig_Znew
            Mt0 = sparse(dimp,dimp*sig_Z^2);
            for k = 1:sig_Z
                Mt0(:, (dimp*sig_Z)*(k-1)+1:(dimp*sig_Z)*k) = kron(eye(dimp),T(j,(sig_Z)*(k-1)+1:(sig_Z)*k));
            end
            Tf((j-1)*dimp^2+1:j*dimp^2,:) = kron(eye(dimp),Mt0);

            if R1sum(j)==1
                Sv((j-1)*dimp^2+1:j*dimp^2)= reshape(sos.expr.b{i}( dimp*(ii-1)+1:dimp*ii,:)',dimp^2,1);
                if ii<size(Zcheck,1)
                    ii = ii+1;
                end
            else
                sos.expr.At{i} = [ sos.expr.At{i}(:,1:(j-1)*dimp^2) sparse(size(sos.expr.At{i},1),dimp^2) sos.expr.At{i}(:,(j-1)*dimp^2+1:end)];
            end
        end

        lidx = sos.extravar.idx{var};
        uidx = sos.extravar.idx{var+1}-1;
        sos.expr.At{i}(lidx:uidx,:) =  Tf';
        sos.expr.b{i} =  Sv;

    end
end;

% ====================================================================================
function sos = addextrasosvar2(sos,I)
% Adding slack SOS variables type II

numstates = size(sos.expr.Z{1},2);
for i = I
    % Creating extra variable
    maxdeg = full(max(sum(sos.expr.Z{i},2)));
    mindeg = full(min(sum(sos.expr.Z{i},2)));
    Z = monomials(numstates,[floor(mindeg/2):ceil(maxdeg/2)]);

    % Discarding unnecessary monomials
    maxdegree = max(sos.expr.Z{i},[],1)/2;
    mindegree = min(sos.expr.Z{i},[],1)/2;
    j = 1;
    while (j <= size(Z,1))
        Zdummy1 = maxdegree-Z(j,:);
        Zdummy2 = Z(j,:)-mindegree;
        idx = find([Zdummy1, Zdummy2]<0);
        if ~isempty(idx)
            Z = [Z(1:j-1,:); Z(j+1:end,:)];
        else
            j = j+1;
        end;
    end;

    % Add the variables

    for k = 0:1

    sos.extravar.num = sos.extravar.num + 1;
    var = sos.extravar.num;
    sos.extravar.Z{sos.extravar.num} = makesparse(Z);
    [T,ZZ] = getconstraint(Z);
    sos.extravar.ZZ{var} = ZZ;
    sos.extravar.T{var} = T';
    sos.extravar.idx{var+1} = sos.extravar.idx{var}+size(Z,1)^2;
    for j = 1:sos.expr.num
        sos.expr.At{j} = [sos.expr.At{j}; ...
                sparse(size(sos.extravar.T{var},1),size(sos.expr.At{j},2))];
    end;

    % Modifying expression
    degoffset = [k sparse(1,numstates-1)];
    pc.Z = sos.extravar.ZZ{var} + degoffset(ones(size(sos.extravar.ZZ{var},1),1),:);
    pc.F = -speye(size(pc.Z,1));
    [R1,R2,newZ] = findcommonZ(sos.expr.Z{i},pc.Z);
    % JFS 6/3/2003: Ensure correct size:
    if isempty(sos.expr.At{i})
       sos.expr.At{i} = sparse(size(sos.expr.At{i},1),size(R1,1));
    end
    %------------
    sos.expr.At{i} = sos.expr.At{i}*R1;
    lidx = sos.extravar.idx{var};
    uidx = sos.extravar.idx{var+1}-1;
    sos.expr.At{i}(lidx:uidx,:) = sos.expr.At{i}(lidx:uidx,:) - sos.extravar.T{var}*pc.F*R2;
    sos.expr.b{i} = R1'*sos.expr.b{i};
    sos.expr.Z{i} = newZ;

    Z = Z(find(Z<maxdegree));    % Discard the unnecessary monomial for the second variable

    end;

end;

% ====================================================================================
function [At,b,K,RR] = processvars(sos,Atf,bf)
% Processing all variables

% Decision variables
K.s = [];
K.f = sos.var.idx{1}-1;
RR = speye(K.f);

% Polynomial and SOS variables
for i = 1:sos.var.num
    switch sos.var.type{i}
    case 'poly'
        sizeX = sos.var.idx{i+1}-sos.var.idx{i};
        startidx = sos.var.idx{i};
        RR = spantiblkdiag(RR,speye(sizeX));
        K.f = sizeX+K.f;
    case 'sos'
        sizeX = sqrt(sos.var.idx{i+1}-sos.var.idx{i});
        startidx = sos.var.idx{i};
        RR = spblkdiag(RR,speye(sizeX^2));
        K.s = [K.s sizeX];
    end;
end;

for i = 1:sos.extravar.num
    sizeX = sqrt(sos.extravar.idx{i+1}-sos.extravar.idx{i});
    startidx = sos.extravar.idx{i};
    RR = spblkdiag(RR,speye(sizeX^2));
    K.s = [K.s sizeX];
end;

At = RR'*Atf;

b = bf;


  function [net, info] = cnn_single_rgb(varargin)
%CNN_SINGLE_RGB Demonstrates fine-tuning a pre-trained CNN with static
% RGB frames (SI in pami journal) on UCF101 dataset

run(fullfile(fileparts(mfilename('fullpath')), ...
  '..', 'matconvnet', 'matlab', 'vl_setupnn.m')) ;

addpath Layers Datasets

opts.dataDir = fullfile('data','UCF101') ;
opts.expDir  = fullfile('exp', 'UCF101') ;
opts.modelPath = fullfile('models','resnext_50_32x4d-pt-mcn.mat');
opts.datasetFn = @cnn_ucf101_setup_data ;
opts.networkFn = @cnn_init_resnext ;
opts.pool1Type = 'none' ;
opts.pool1Layer = 'conv1' ;
opts.pool2Layer = '' ;
[opts, varargin] = vl_argparse(opts, varargin) ;

opts.numFetchThreads = 8 ;

opts.lite = false ;
opts.imdbPath = fullfile(opts.dataDir, 'imdb-rgb.mat');
opts.ARPoolLayer = 'conv0'; % before conv1
opts.DropOutRate = 0.5 ;
opts.epochFactor = 5 ;

opts.split = 1; % data split
opts.reverseDyn = 0; % reverse video frames e.g.[N:-1:1]
opts.train = struct() ;
opts.train.gpus = [];
opts.train.batchSize = 128 ;
opts.train.numSubBatches = 16 ;
opts.train.solver = [] ;
opts.train.prefetch = true ;
opts.train.numEpochs = 30 ;
% resnet50
opts.train.learningRate = 1e-2 * [ones(1,2), 0.1*ones(1,1), 0.01*ones(1,1)];
% caffe-ref
opts.train.learningRate = 1e-4 * [ones(1,2), 0.1*ones(1,1), 0.01*ones(1,1)];

opts = vl_argparse(opts, varargin) ;
if ~isfield(opts.train, 'gpus'), opts.train.gpus = []; end;
% opts.train.numEpochs = numel(opts.train.learningRate);

% -------------------------------------------------------------------------
%                                                              Prepare data
% -------------------------------------------------------------------------

if exist(opts.imdbPath,'file')
  imdb = load(opts.imdbPath) ;
else
  imdb = opts.datasetFn('dataDir', opts.dataDir, 'lite', opts.lite) ;
  mkdir(opts.expDir) ;
  save(opts.imdbPath, '-struct', 'imdb') ;
end

% UCF101 has 3 data splits
if opts.split>3
  error('split should be <=3');
end
imdb.images.set = imdb.images.sets(opts.split,:);

% reverse frame order
if opts.reverseDyn
  for i=1:numel(imdb.images.names)
    imdb.images.names{i} = imdb.images.names{i}(end:-1:1);
  end
end

% -------------------------------------------------------------------------
%                                                             Prepare model
% -------------------------------------------------------------------------
net = load(opts.modelPath);
if isfield(net,'net')
  net = net.net;
end
opts.nCls = max(imdb.images.label) ;
net = opts.networkFn(net,opts);

if numel(net.meta.normalization.averageImage)>3
  sz = size(net.meta.normalization.averageImage) ;
  net.meta.normalization.averageImage = ...
    mean(reshape(net.meta.normalization.averageImage,[sz(1)*sz(2) sz(3)]),1) ;
end

% Set the class names in the network
net.meta.classes.name = imdb.classes.name ;
net.meta.classes.description = imdb.classes.name ;
% -------------------------------------------------------------------------
%                                                                     Learn
% -------------------------------------------------------------------------
if opts.epochFactor>0
  opts.train.train = repmat(find(imdb.images.set==1),[1 opts.epochFactor]) ;
else
  opts.train.train = NaN ;
end
opts.train.val = find(imdb.images.set==3) ;

[net, info] = cnn_train_dicnn_dag(net, imdb, getBatchFn(opts, net.meta), ...
                      'expDir', opts.expDir, ...
                      opts.train) ;

% -------------------------------------------------------------------------
%                                                          Report accuracy
% -------------------------------------------------------------------------
errlayer = net.getLayerIndex('errMC') ;

if ~isnan(errlayer)
  cats = imdb.classes.name ;
  accs = net.layers(errlayer).block.accuracy ;

  if numel(cats)~=numel(accs)
    error('wrong number of classes\n') ;
  end

  for i=1:numel(cats)
    fprintf('%s acc %.1f\n',cats{i},100*accs(i)) ;
  end
  fprintf('Mean accuracy %.1f\n',100*mean(accs)) ;
end

% -------------------------------------------------------------------------
function fn = getBatchFn(opts, meta)
% -------------------------------------------------------------------------
useGpu = numel(opts.train.gpus) > 0 ;

bopts.numThreads = opts.numFetchThreads ;
bopts.imageSize = meta.normalization.imageSize ;
if isfield(meta.normalization,'border')
  bopts.border = meta.normalization.border ;
else
  bopts.border = meta.normalization.imageSize(1:2) ./ ...
    meta.normalization.cropSize - meta.normalization.imageSize(1:2);

end

% bopts.averageImage = [];
bopts.averageImage = meta.normalization.averageImage ;
bopts.interpolation = meta.normalization.interpolation ;
bopts.keepAspect = meta.normalization.keepAspect ;
% bopts.rgbVariance = meta.augmentation.rgbVariance ;
% bopts.transformation = meta.augmentation.transformation ;

fn = @(x,y) getDagNNBatch(bopts,useGpu,x,y) ;

% -------------------------------------------------------------------------
function inputs = getDagNNBatch(opts, useGpu, imdb, batch)
% -------------------------------------------------------------------------

% batch refers to videos (not for frames)
if isempty(batch)
  inputs = {'input', [], 'label', [], 'VideoId1', [], 'VideoId2', []};
  return;
end

isVal = ~isempty(batch) && imdb.images.set(batch(1)) ~= 1 ;

% if ~isVal, transformation='stretch'; else transformation='none';end
if ~isVal, transformation='multiScaleRegular'; else transformation='none';end

names = imdb.images.names(batch);

% images = strcat([imdb.imageDir filesep], imdb.images.name(batch)) ;

namesM = {};
nVids = numel(batch);

VideoId1 = [];
VideoId2 = [];

% step-size
stepSize = 6;
% pool nFrames into a dynamic image
nFrames = 1;
% number of dynamic images to be max pooled later
nDynImgs = 10;

c1 = 1;
for v=1:nVids

  name = names{v};
  nFrms = numel(name);

  nSample = nFrames;
  nr = numel(1:stepSize:nFrms);

  % jitter by removing 50 % and limit a batch to nMaxs * nSamples images
  if nr > 1 && (~isVal && nr>nDynImgs)
    rat = min(nDynImgs,ceil(0.50*nr));
    ri = randperm(nr);
    ri = ri(1:rat);
    r = zeros(1,nr);
    r(ri) = 1;
  else
    r = ones(1,nr);
  end

  c3 = 1;
  c2 = 0;

  for f=1:stepSize:nFrms
    if r(c3)
      idx = f:min(f+nSample-1,nFrms) ;
      if numel(idx)<nFrames
        idx = [idx idx(end) * ones(1,nFrames-numel(idx))];
      end
      namesM{end+1} = name(idx);
      VideoId1 = [VideoId1 c1 * ones(1,numel(idx))];
      c1 = c1 + 1;
      c2 = c2 + 1;
    end
    c3 = c3 + 1;
  end
  VideoId2 = [VideoId2 v * ones(1,c2) ] ;
end

images = strcat([imdb.imageDir filesep], horzcat(namesM{:}) ) ;

im = cnn_video_rgb_get_batch(images, VideoId1, opts, ...
  'transformation', transformation, 'prefetch', nargout == 0, ...
  'subMean', false) ;

if nargout > 0
  if useGpu
    im = gpuArray(im) ;
  end
  inputs = {'input', im, 'label', imdb.images.label(batch), ...
    'VideoId2', VideoId2};
end


clear all;close all;clc

M_dir = GetFishDirectories();
save_masterdir = GetNestedDataDir();

range_fish = GetFishRange();

for i_fish = range_fish,
    disp(['i_fish = ' num2str(i_fish)]);

    data_dir = M_dir{i_fish};

    %% init
    save_dir = fullfile(save_masterdir,['subject_' num2str(i_fish)]);
    load(fullfile(save_dir,'CoreInfo.mat'));
    load(fullfile(save_dir,'OptionalInfo.mat'));

    %% get anatomy projections
    % x-y view
    im = max(anat_stack,[],3);
    out=imNormalize99(im);
    anat_yx = repmat(out,[1 1 3]);

    % y-z view
    im = squeeze(max(anat_stack,[],2));
    out=imNormalize99(im);
    anat_yz = repmat(out,[1 1 3]);

    % x-z view
    im = squeeze(max(anat_stack,[],1));
    out=imNormalize99(im);
    out = flipud(out'); %%%% empirically necessary...
    anat_zx = repmat(out,[1 1 3]);

    %% [Next 2 cells] delete anatomically out-of-bound cells by hand
    % only execute once (manual choice 1):
    cHolder_Anat = []; % collect cIX of all out-of-bound cells

    %% (optional) step 1: set limit to only plot ventral layer, helps to find outliers within that layer
    % draw cells first
    % (choose start and stop index to draw; low numbers ~ ventral)
    I_start = 1;
    I_stop = 1*round(numcell_full/10);

    % plot those cells
    cIX = I_start:I_stop;
    gIX = round(cIX/1000)+1;
    figure('Position',[100 0 1300 900]);
    numK = round(cIX(end)/1000)+1;
    [~, dim_totimage] = BasicDrawCellsOnAnatProj(CellXYZ,cIX,gIX,numK,anat_yx,anat_yz);%,anat_zx,'hsv','full');

    % Here manually draw polygons around cells to discard.
    % Double click to connect last vertex to first vertex, then double click again within polygon to fix.
    % then click again to start drawing the next one
    % and when finished, break loop by pressing any key

    for i = 1:100, % NOMINAL LOOP, break manually
        h_poly_yx = impoly;
        wait(h_poly_yx); % double click to finalize position!
        % update finalized polygon in bright color
        setColor(h_poly_yx,[0 1 1]);

        A = sub2ind(size(anat_yx),CellXYZ(I_start:I_stop,1),CellXYZ(I_start:I_stop,2));
        MaskArray = createMask(h_poly_yx);
        B = find(MaskArray); % find indices of pixels within ROI
        cIX2 = find(ismember(A,B));
        cHolder_Anat = union(cHolder_Anat,cIX2);
        w = waitforbuttonpress;
        if w == 1, % press any key to break
            break;
        end
    end

    %% step 2: plot all cells
    I_start = 1; % needs to be 1 for correct indexing
    I_stop = numcell_full;

    % ...here until the end of the cell is the exact dupliate of the last cell...
    cIX = I_start:I_stop;
    gIX = round(cIX/1000)+1;
    figure('Position',[100 0 1300 900]);
    numK = round(cIX(end)/1000)+1;
    [tot_image, dim_totimage] = BasicDrawCellsOnAnatProj(CellXYZ,cIX,gIX,numK,anat_yx,anat_yz);%,anat_zx,'hsv','full');

    % Here manually draw polygons around cells to discard.
    % Double click to connect last vertex to first vertex, then double click again within polygon to fix.
    % then click again to start drawing the next one
    % and when finished, break loop by pressing any key

    for i = 1:100, % NOMINAL LOOP, break manually
        h_poly_yx = impoly;
        wait(h_poly_yx); % double click to finalize position!
        % update finalized polygon in bright color
        setColor(h_poly_yx,[0 1 1]);

        A = sub2ind(size(anat_yx),CellXYZ(I_start:I_stop,1),CellXYZ(I_start:I_stop,2));
        MaskArray = createMask(h_poly_yx);
        B = find(MaskArray); % find indices of pixels within ROI
        cIX2 = find(ismember(A,B));
        cHolder_Anat = union(cHolder_Anat,cIX2);
        w = waitforbuttonpress;
        if w == 1, % press any key to break
            break;
        end
    end

    %% find extra outliers on borders of image (can't always get with polygon)
    IX = find(CellXYZ(:,1)<15 | CellXYZ(:,1)> size(anat_stack,1)-15 ...
        | CellXYZ(:,2)<15 | CellXYZ(:,2)> size(anat_stack,2)-15);
    % for fish 9, eliminating bad plans: | CellXYZ(:,3)<4 | CellXYZ(:,3)> size(anat_stack,3)-3);
    cHolder_Anat = union(cHolder_Anat,IX);

    %% test Plot: all antomy outliers
    IX_inval_anat = cHolder_Anat; % rename
    cIX = IX_inval_anat;
    gIX = (1:length(cIX))';
    figure;
    BasicDrawCellsOnAnatProj(CellXYZ,cIX,gIX,numK,anat_yx,anat_yz);
    pause(2);

    %% test Plot: all remaining cells
    I_v_Holder = ones(1,numcell_full);
    I_v_Holder(IX_inval_anat) = 0;
    cIX = find(I_v_Holder);
    gIX = (1:length(cIX))';
    figure;
    BasicDrawCellsOnAnatProj(CellXYZ,cIX,gIX,numK,anat_yx,anat_yz);
    pause(2);

    %% ...and save
    temp = fullfile(data_dir,['Fish' num2str(i_fish) '_extrainfo_anat.mat']);
    save(temp,'IX_inval_anat');

    %%

    % save_masterdir = GetNestedDataDir();
    % save_dir = fullfile(save_masterdir,['subject_' num2str(i_fish)]);

    IX_inval = IX_inval_anat;
    filename = fullfile(save_dir,'OptionalInfo.mat');
    save(filename,'IX_inval','-append');

    filename = fullfile(save_dir,'AdditionalInfo.mat');
    save(filename,'IX_inval_anat','-append');
    disp('anat outliers saved');

end


function varargout = PlotComet_3D(x_data, y_data, z_data, varargin)

%function creates a moving comet plot in 3 dimensions using the specificed
%x, y, and z line data
%
%PlotComet_3D(x_data, y_data, z_data)
%
%Optional Input Arguements:
%cFigure, handle to the current figure or axes to create the plot in (will
%   add the line data to the plot regardless of whether hold is on
%
%Frequency, playback frequency, if not provided assumed to be 10 Hz
%
%blockSize, number of points in the comet tail, assumed to be 1/20 of
%   length(x_data) if not provided
%
%PlotComet_3D(x_data, y_data, z_data, 'cFigure',FigureHandle,...
%     'Frequency',freq, 'blockSize',num_points)
%
%tailFormat, a structured variable containing the fomrating requirements of
%   the tail (if different than the default red solid line)
%   ex: Tail = struct('LineWidth',2,'Color','g','LineStyle','*');
%   PlotComet_3D(x_data,y_data,z_data,'tailFormat',Tail);
%
%headFormat, a structued vairable containing the formating requirements of
%   the head (if different than the default blue circle)
%   ex: Head = struct('LineStyle','none','MarkerSize',8,'Marker','^','Color','k');
%   PlotComet_3D(x_data,y_data,z_data,'headFormat',head);
%   Note: 'LineSytle','none' is required since the head is plotted as a
%   single point!
%
%Optional Output Arguements
%hFigure = figure handle to the comet plot
%
%Example: Create a surface plot and construct a moving line plot through it
%with connected cyan stars and dashed line on a 50 points long tail
%and large green square for a head.
%
%%create some data for the surface
%[X, Y] = meshgrid(linspace(-1, 1, 25), linspace(-1, 1, 25));
%Z = X .* exp(-X.^2 - Y.^2);
%%open a new figure and plot the surface
%surf(X, Y, Z);
%fig = gcf;
%%create the line data
%t = -pi:pi/500:pi;
%x = sin(5*t);
%y = cos(3*t);
%z = t;
%%define the tail formated structure
%Tail = struct('LineStyle','--','Marker','*','Color','c',...
%   'LineWidth',2,'MarkerSize',4);
%%define the head formated structure
%Head = struct('LineStyle','none','Marker','s','Color','g','MarkerSize',10);
%
%%Invoke the comet plot with 50 Hz playback, and a tail length of 50 pts
%PlotComet_3D(x,y,z,'cFigure',fig,'blockSize',50,'Frequency',50,...
%   'headFormat',Head,'tailFormat',Tail);
%
%Nick Hansford
%09/26/2013

%initialize the inputs
freq = 10;
blockSize = floor(1/20*length(x_data));
tailFormat = struct('LineWidth',1,'Color','r','LineStyle','-');
headFormat = struct('LineStyle','none','Marker','o','MarkerSize',6,...
    'Color','b');

%parse out the inputs
argCount = nargin - 3;

for i = 1:2:argCount
    %         caseVar = varargin{i}
    switch varargin{i}
        case 'cFigure'
            cFigure = varargin{i+1};
            %get the original size:
            cAxes = get(cFigure,'CurrentAxes');
            xLim = get(cAxes,'XLim');
            yLim = get(cAxes,'YLim');
            zLim = get(cAxes,'ZLim');

            %resize if the new plot will exceed them
            if xLim(1) > min(x_data)
                xLim(1) = min(x_data);
            end

            if xLim(2) < max(x_data)
                xLim(2) = max(x_data);
            end

            if yLim(1) > min(y_data)
                yLim(1) = min(y_data);
            end

            if yLim(2) < max(y_data)
                yLim(2) = max(y_data);
            end

            if zLim(1) > min(z_data)
                zLim(1) = min(z_data);
            end

            if zLim(2) < max(z_data)
                zLim(2) = max(z_data);
            end

            axis([xLim, yLim, zLim]);

        case 'blockSize'
            blockSize = varargin{i+1};
        case 'Frequency'
            freq = varargin{i+1};

        case 'tailFormat'
            tailFormat = varargin{i+1};

        case 'headFormat'
            headFormat = varargin{i+1};
    end
end

%make sure the figure exists, if not, create it
if ~exist('cFigure')
    cFigure = figure;
    view(3);
    axis([min(x_data), max(x_data),...
        min(y_data), max(y_data),...
        min(z_data), max(z_data)]);
end

%user can pass in current axes, if so, get the parent for the figure window
%and use that instead...should make this compatible with GUIs?
if strcmp(get(cFigure,'Type'),'axes')
    cFigure = get(cFigure,'Parent');
end

%activate the figure window
figure(cFigure);
cAxes = get(cFigure,'CurrentAxes');

oldNextPlot = get(cAxes,'NextPlot');
set(cAxes,'NextPlot','add');

pauseTime = 1./freq;

n_start = 1;
n_stop = 1;
%put on the starting point
plot3(x_data(n_start:n_stop),...
    y_data(n_start:n_stop),...
    z_data(n_start:n_stop),tailFormat);
plot3(x_data(n_stop), y_data(n_stop), z_data(n_stop),headFormat);

%playback!
for n = 1:1:length(x_data)
    a = tic;
    %delete the previous plot
    hChild = get(cAxes,'Children');
    delete(hChild(1:2));

    if n <= blockSize
        n_start = 1;
        n_stop = n;
    else
        n_start = n_start + 1;
        n_stop = n_stop + 1;
    end

    %new plot
    plot3(x_data(n_start:n_stop),...
        y_data(n_start:n_stop),...
        z_data(n_start:n_stop),tailFormat);
    plot3(x_data(n_stop), y_data(n_stop), z_data(n_stop),headFormat);
    drawnow;

    %update playback refresh rate
    b = toc(a);
    pause(pauseTime-b);
end

set(cAxes,'NextPlot',oldNextPlot);
% fprintf('Finished\n');

if nargout == 1
    varargout{1} = cFigure;
end


function [varargout] = ft_plot_patch(hdat, vdat, varargin)

% FT_PLOT_PATCH plot a colored shape, similar to the MATLAB patch() function. It is
% similar in usage as ft_plot_vector, and they can be combined, for example,
% to plot an area equivalent to a SEM or STD-DEV around a line.
%
% Use as
%   ft_plot_patch(X, Y, ...)
% where X and Y are similar as the input to the MATLAB patch() function.
%
% Optional arguments should come in key-value pairs and can include
%   'axis'            = draw the local axis,  can be 'yes', 'no', 'xy', 'x' or 'y'
%   'box'             = draw a box around the local axes, can be 'yes' or 'no'
%   'tag'             = string, the name assigned to the object. All tags with the same name can be deleted in a figure, without deleting other parts of the figure.
%   'facecolor'       = see MATLAB standard patch properties
%   'facealpha'       = see MATLAB standard patch properties (note, approx. transparency can be achieved using 'facecolor')
%   'edgecolor'       = see MATLAB standard patch properties (default is 'none') (equivalent to 'linecolor' in PLOT)
%   'linestyle'       = see MATLAB standard patch properties
%   'linewidth'       = see MATLAB standard patch properties
%
% The color of the patchand the edges (i.e. border lines) can be specified in a variety of ways
%   - as a string with one character per line that you want to plot. Supported colors are the same as in PATCH, i.e. 'bgrcmykw'.
%   - as an 'RGB triplet', a 1x3 vector with values between 0 and 1
%   - as 'none' if you do not want the face of the patch to be filled (useful when you want to plot an empty box).
%
% It is possible to plot the object in a local pseudo-axis (c.f. subplot), which is specfied as follows
%   'hpos'            = horizontal position of the center of the local axes
%   'vpos'            = vertical position of the center of the local axes
%   'width'           = width of the local axes
%   'height'          = height of the local axes
%   'hlim'            = horizontal scaling limits within the local axes
%   'vlim'            = vertical scaling limits within the local axes
%
% Example
%   hdat = [1:10 10:-1:1];
%   vdat = rand(1,10);
%   vdat = [vdat vdat(end:-1:1)+1];
%   ft_plot_patch(hdat, vdat)
%
% See also FT_PLOT_VECTOR, PATCH, PLOT

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

ws = warning('on', 'MATLAB:divideByZero');

% get the optional input arguments
hpos            = ft_getopt(varargin, 'hpos');
vpos            = ft_getopt(varargin, 'vpos');
width           = ft_getopt(varargin, 'width');
height          = ft_getopt(varargin, 'height');
hlim            = ft_getopt(varargin, 'hlim', 'maxmin');
vlim            = ft_getopt(varargin, 'vlim', 'maxmin');
axis            = ft_getopt(varargin, 'axis', false);
box             = ft_getopt(varargin, 'box', false);
tag             = ft_getopt(varargin, 'tag', '');
parent          = ft_getopt(varargin, 'parent', []);
facecolor       = ft_getopt(varargin, 'facecolor', 'b');
facealpha       = ft_getopt(varargin, 'facealpha', 1);
edgecolor       = ft_getopt(varargin, 'edgecolor', 'none');
linestyle       = ft_getopt(varargin, 'linestyle', 'none');
linewidth       = ft_getopt(varargin, 'linewidth', .5);

% convert the yes/no strings into boolean values
box = istrue(box);

% color management
if ischar(facecolor) && exist([facecolor '.m'], 'file')
  facecolor = eval(facecolor);
end
if ischar(edgecolor) && exist([edgecolor '.m'], 'file')
  edgecolor = eval(edgecolor);
end

% this should be a string, because valid options include yes, no, xy, x, y
if isequal(axis, true)
  axis = 'yes';
elseif isequal(axis, false)
  axis = 'no';
end

% everything is added to the current figure
holdflag = ishold;
if ~holdflag
  hold on
end

if ischar(hlim)
  switch hlim
    case 'maxmin'
      hlim = [min(hdat) max(hdat)];
    case 'maxabs'
      hlim = max(abs(hdat));
      hlim = [-hlim hlim];
    otherwise
      ft_error('unsupported option for hlim')
  end % switch
end % if ischar

if ischar(vlim)
  switch vlim
    case 'maxmin'
      vlim = [min(vdat(:)) max(vdat(:))];
    case 'maxabs'
      vlim = max(abs(vdat(:)));
      vlim = [-vlim vlim];
    otherwise
      ft_error('unsupported option for vlim')
  end % switch
end % if ischar

if vlim(1)==vlim(2)
  % vertical scaling cannot be determined, behave consistent to the plot() function
  vlim = [-1 1];
end

% these must be floating point values and not integers, otherwise the scaling fails
hdat = double(hdat);
vdat = double(vdat);
hlim = double(hlim);
vlim = double(vlim);

if isempty(hpos) && ~isempty(hlim)
  hpos = (hlim(1)+hlim(2))/2;
end
if isempty(vpos) && ~isempty(vlim)
  vpos = (vlim(1)+vlim(2))/2;
end

if isempty(width) && ~isempty(hlim)
  width = hlim(2)-hlim(1);
end

if isempty(height) && ~isempty(vlim)
  height = vlim(2)-vlim(1);
end

% first shift the horizontal axis to zero
if any(hlim) ~= 0
  hdat = hdat - (hlim(1)+hlim(2))/2;
  % then scale to length 1
  if hlim(2)-hlim(1)~=0
    hdat = hdat ./ (hlim(2)-hlim(1));
  else
    hdat = hdat /hlim(1);
  end
  % then scale to the new width
  hdat = hdat .* width;
end
% then shift to the new horizontal position
hdat = hdat + hpos;

if any(vlim) ~= 0
  % first shift the vertical axis to zero
  vdat = vdat - (vlim(1)+vlim(2))/2;
  % then scale to length 1
  vdat = vdat / (vlim(2)-vlim(1));
  % then scale to the new width
  vdat = vdat .* height;
end
% then shift to the new vertical position
vdat = vdat + vpos;

% plot the patch
h = patch(hdat, vdat, facecolor, 'facealpha', facealpha, 'edgecolor', edgecolor, 'linestyle', linestyle, 'linewidth', linewidth);

if box
  % this plots a box around the original hpos/vpos with appropriate width/height
  x1 = hpos - width/2;
  x2 = hpos + width/2;
  y1 = vpos - height/2;
  y2 = vpos + height/2;

  X = [x1 x2 x2 x1 x1];
  Y = [y1 y1 y2 y2 y1];
  h = line(X, Y);
  set(h, 'color', 'k');

  % this plots a box around the original hpos/vpos with appropriate width/height
  % boxposition = zeros(1,4);
  % boxposition(1) = hpos - width/2;
  % boxposition(2) = hpos + width/2;
  % boxposition(3) = vpos - height/2;
  % boxposition(4) = vpos + height/2;
  % ft_plot_box(boxposition, 'facecolor', 'none', 'edgecolor', 'k');

  % this plots a box around the complete data
  % boxposition = zeros(1,4);
  % boxposition(1) = hlim(1);
  % boxposition(2) = hlim(2);
  % boxposition(3) = vlim(1);
  % boxposition(4) = vlim(2);
  % ft_plot_box(boxposition, 'hpos', hpos, 'vpos', vpos, 'width', width, 'height', height, 'hlim', hlim, 'vlim', vlim);
end

if ~isempty(axis) && ~strcmp(axis, 'no')
  switch axis
    case {'yes' 'xy'}
      xaxis = true;
      yaxis = true;
    case {'x'}
      xaxis = true;
      yaxis = false;
    case {'y'}
      xaxis = false;
      yaxis = true;
    otherwise
      ft_error('invalid specification of the "axis" option')
  end

  if xaxis
    % x-axis should touch 0,0
    xrange = hlim;
    if sign(xrange(1))==sign(xrange(2))
      [dum minind] = min(abs(hlim));
      xrange(minind) = 0;
    end
    ft_plot_line(xrange, [0 0], 'hpos', hpos, 'vpos', vpos, 'hlim', hlim, 'vlim', vlim, 'width', width, 'height', height);
  end
  if yaxis
    % y-axis should touch 0,0
    yrange = vlim;
    if sign(yrange(1))==sign(yrange(2))
      [dum minind] = min(abs(vlim));
      yrange(minind) = 0;
    end
    ft_plot_line([0 0], yrange, 'hpos', hpos, 'vpos', vpos, 'hlim', hlim, 'vlim', vlim, 'width', width, 'height', height);
  end
end

set(h, 'tag', tag);

if ~isempty(parent)
  set(h, 'Parent', parent);
end

% the (optional) output is the handle
if nargout == 1;
  varargout{1} = h;
end

if ~holdflag
  hold off
end

warning(ws); % revert to original state


function model = rmGridFit_oneGaussian(model,prediction,data,params,t)
% rmGridFit_oneGaussian - core of one Gaussian fit
%
% model = rmGridFit_oneGaussian(model,prediction,data,params);
%
% 2008/01 SOD: split of from rmGridFit.

% input check
if nargin < 4,
    error('Not enough arguments');
end

% some variables we need
%rssinf         = inf(size(data(1,:)),'single');
trends         = t.trends;
t_id           = t.dcid+1;

% we compute mean rss from the sum rss (old convention)
% Some explanation of the divisor would be helpful here.
model.rss = single(model.rss./(size(prediction,1)-(size(trends,2)+1)));

%-----------------------------------
%--- fit different receptive fields profiles
%--- another loop --- and a slow one too
%-----------------------------------
tic; progress = 0;

%warning('off', 'MATLAB:lscov:RankDefDesignMat')

for n=1:numel(params.analysis.x0),
    %-----------------------------------
    % progress monitor (10 dots) and time indicator
    %-----------------------------------
    if floor(n./numel(params.analysis.x0).*10)>progress,
        if progress==0,
            % print out estimated time left
            esttime = toc.*10;
            if floor(esttime./3600)>0,
                fprintf(1,'[%s]:Estimated processing time: %d hours.\t(%s)\n',...
                    mfilename, ceil(esttime./3600), datestr(now));
            else
                fprintf(1, '[%s]:Estimated processing time: %d minutes.\t(%s)\n',...
                    mfilename, ceil(esttime./60), datestr(now));
            end;
            fprintf(1,'[%s]:Grid (x,y,sigma) fit:',mfilename);drawnow;
        end;
        % progress monitor
        fprintf(1,'.');drawnow;
        progress = progress + 1;
    end;

    %-----------------------------------
    %--- now apply glm to fit RF
    %-----------------------------------
    % minimum RSS fit

    X    = [prediction(:,n) trends];  % Build the model matrix

    % This line takes up 30% of the time
    % lscov takes as long as the pinv method but provides the rss as
    % well... Let's put in the ~.  All modern Matlab now uses it, and it
    % will save us memory space.
    [b,ci,rss]    = lscov(X,data);

    % Compute RSS only for positive fits. The basic problem is
    % that if you have two complementary locations, you
    % could fit with a postive beta on the one that drives the signal or a
    % negative beta on the portion of the visual field that never sees the
    % stimulus. This would produce the same prediction. We don't like that
    nkeep   = (b(1,:)<0); % Now we only set the negative fits to inf.

    % To save time limit the rss computation to those we care about.
    rss(nkeep) = inf('single');

    %-----------------------------------
    %--- store data with lower rss
    %-----------------------------------
    minRssIndex = (rss < model.rss);  % This is where we use model.rss

    % now update model parameters for those cases in which the computed rss
    % is smaller than the input model.rss
    model.x0(minRssIndex)       = params.analysis.x0(n);
    model.y0(minRssIndex)       = params.analysis.y0(n);
    model.s(minRssIndex)        = params.analysis.sigmaMajor(n);
    model.s_major(minRssIndex)        = params.analysis.sigmaMajor(n);
    model.s_minor(minRssIndex)        = params.analysis.sigmaMajor(n);
    model.s_theta(minRssIndex)        = params.analysis.theta(n);
    model.rss(minRssIndex)      = rss(minRssIndex);
    model.b([1 t_id],minRssIndex) = b(:,minRssIndex);
end

%warning('on', 'MATLAB:lscov:RankDefDesignMat')

% Under some conditions, the grid fit never returns an acceptable fit, For
% example for onegaussian fits with data driven DC component, when the DC
% is artificially high. In this case some of the rss values remain Inf,
% which fails to interpolate and compute correct variance explained values.
% So we check it here and reset any Inf (bad fits) to rawrss, so the
% variance explained will be 0.
model.rss(model.rss==Inf) = model.rawrss(model.rss==Inf);

% Correct lscov. It returns the mean rss. To maintain compatibility with
% the sum rss that this function expects, we have to multiply by the
% divisor. See the lscov docs for details.
model.rss = single(model.rss.*(size(prediction,1)-(size(trends,2)+1)));

% end time monitor
et  = toc;
if floor(esttime/3600)>0,
    fprintf(1,'Done[%d hours].\t(%s)\n', ceil(et/3600), datestr(now));
else
    fprintf(1,'Done[%d minutes].\t(%s)\n', ceil(et/60), datestr(now));
end;
drawnow;

return;