function [ status ] = export_poscar( filename, geometry )
%EXPORT_POSCAR Export a geometry struct as a VASP POSCAR file.
%   status = EXPORT_POSCAR(filename,geometry) exports the geometry as a
%   VASP POSCAR file.
%
%   See also IMPORT_POSCAR.

    % write selective dynamics and chemical symbols

    fid = fopen(filename,'w');
    if fid==-1
        error(['Error opening ' filename]);
    end

    fprintf(fid,[geometry.comment '\n']);
    fprintf(fid,'1.0\n'); % scale factor
    fprintf(fid, '%19.16f %19.16f %19.16f\n', geometry.lattice'); % lattice vectors
    if ~isempty(geometry.symbols)
        cellfun(@(x) fprintf(fid, '%s ', x), geometry.symbols);
        fprintf(fid, '\n');
    end
    fprintf(fid, '%d ', geometry.atomcount); % number of each species
    fprintf(fid, '\nDirect\n');
    fprintf(fid, '%19.16f %19.16f %19.16f\n', geometry.coords');

    fclose(fid);

    status = 0;

end


%
% Optimize the order of strokes in a MotorProgram,
% before the relations are fixed
%
% Input
%  Q: reference to MotorProgram object
%
function optimize_order_MP(Q,lib)

    ps = defaultps_bottomup;

    % get all the permutations to try
    if Q.ns <= ps.max_ns_all_perm
        % try all possible combinations
        P = perms(1:Q.ns);
    else
        % try a subset of the permutations
        np = factorial(ps.max_ns_all_perm); %720
        P = zeros(np,Q.ns);
        for i=1:np
            P(i,:) = randperm(Q.ns);
        end
        P = unique(P,'rows');
    end

    % score all the permutations
    n = size(P,1);
    scores = zeros(n,1);
    for i=1:n
       QQ = Q.copy();
       perm = P(i,:);
       QQ.S = QQ.S(perm);
       scores(i) =  scoreMP_NoRel(QQ,lib,'type',true,'token',true,'stat',false,'image',false);
    end

    % pick the best order, and return Q without instantiating relations
    [~,windx] = randmax(scores);
    perm = P(windx,:);
    Q.S = Q.S(perm);
end


function x = true(x)
% TRUE Constrains a variable to be positive
%
% TRUE(x) returns the constraint (x>=0.5). It is assumed that x is
% binary. The reason (x>=0.5) is used instead of x==1 is that some big-M
% modelling turns out to be less sensitive to numerical issues in this
% form. Once the model enters the solver, it will be trivially presolved
% anyway.
%
% For safety, it is advised to use the TRUE operator when working with
% logic constraints, instead of relying on the automatic constraints used
% by YALMIP (expression generated using AND and OR are automatically
% assumed to be constrained to be true.
%
% (a|b) is automatically changed to (TRUE(a|b)). To constrain a to be true,
% the user has to explicitely use (TRUE(a)).
%
%   See also SDPVAR/FALSE, SDPVAR/AND, SDPVAR/OR, SDPVAR/NOT, BINVAR, BINARY

x = (x>=.5);


function test_suite = test_it
  initTestSuite;
end

function test_entropy
    data = [0 0 0 1 1 1 2 2 2 3 3 3];
    h = SPX_IT.entropy(data);
    assertElementsAlmostEqual(h, 2.0);
end


% ----- Global evaluation parameters -----
% Range of binning factors used for this evaluation.
binningFactors = [2, 3, 4];
% Range of compression levels (index 1 means uncoded; indices 2-5 mean
% coded with the corresponding QP setting using H.265/HEVC coder.
compressions   = [NaN, 10, 20, 30, 40];
% Range of number of frames for SR at the different binning factors.
numberOfFrames = [5, ...    % Number of frames for binning factor 2
                  11, ...   % Number of frames for binning factor 3
                  17];      % Number of frames for binning factor 4
% Index of the first reference frame for sliding window processing.
startReferenceFrame = 9;

% ----- Settings considered for this evaluation -----
% This is the index of the SR method that is evaluated.
sr_method           = 0:length(SRMethods);
% This is the index of the binning factor that is evaluated.
binning_val         = 1:3;
% This is the index of the compression setting.
compress_val        = 1:5;
% This is the index that indicates the number of input frames.
numberOfFrames_val  = 1;
% This is the index of the scene according to datasets.mat.
scenes_val          = 1:14;
% This is the index of the sliding window that is evaluated.
sliding_val         = 1;


%% Piggyback image
%  Changed: Dec 31st, 2011
%
function [I,lim] = piggyback(I,scale)

    if nargin<2; scale = 2; end; % default, piggybacked image twice as big

    if size(size(I),2)>3; [I,lim]=piggyback_multichannel(I,scale); return; end; % check if I is multichannel

    Ip  = zeros(ceil(size(I)*scale));
    lim = bsxfun(@plus, floor(size(I)*(scale-1)/2), [[1 1 1]; size(I)]); % image limits
    Ip(lim(1):lim(2),lim(3):lim(4),lim(5):lim(6)) = I;                   % piggybacked image
    I = Ip;

end

%% Piggyback image
%  Changed: Dec 31st, 2011
%
function [I,lim] = piggyback_multichannel(I,scale)

    if nargin<2; scale = 2; end; % default, piggybacked image twice as big
    nchannels = size(I,4);

    for i=1:nchannels
        Ip(:,:,:,i) = piggyback(I(:,:,:,i),scale);
    end

    imagesize = [size(I,1) size(I,2) size(I,3)];
    lim = bsxfun(@plus, floor(imagesize*(scale-1)/2), [[1 1 1]; imagesize]); % image limits
    I = Ip;

end


function varargout = line(rot,varargin)
% draw rotations connected by lines
%
% Syntax
%   line(rot,'linecolor','r','linewith',2)
%
% Input
%  rot - list of @rotation
%
% Example
%   cs1 = crystalSymmetry('321')
%   cs2 = crystalSymmetry('432')
%   oR = fundamentalRegion(cs1,cs2)
%   plot(oR)
%
%   % connect to vertices of the fundamental region
%   f = fibre(oR.V(1),oR.V(2))
%
%   % determine some orientations along the fibre
%   o = f.orientation
%
%   % plot the line
%   hold on
%   line(o,'color','r','linewidth',2)
%   hold off
%
% See also
%

[varargout{1:nargout}] = plot(reshape(rot,[],1),'all','edgecolor',...
  get_option(varargin,{'color','linecolor'},'k'),'Marker','none',varargin{:});


%% Machine Learning Online Class
%  Exercise 6 | Spam Classification with SVMs
%
%  Instructions
%  ------------
%
%  This file contains code that helps you get started on the
%  exercise. You will need to complete the following functions:
%
%     gaussianKernel.m
%     dataset3Params.m
%     processEmail.m
%     emailFeatures.m
%
%  For this exercise, you will not need to change any code in this file,
%  or any other files other than those mentioned above.
%

%% Initialization
clear ; close all; clc

%% ==================== Part 1: Email Preprocessing ====================
%  To use an SVM to classify emails into Spam v.s. Non-Spam, you first need
%  to convert each email into a vector of features. In this part, you will
%  implement the preprocessing steps for each email. You should
%  complete the code in processEmail.m to produce a word indices vector
%  for a given email.

fprintf('\nPreprocessing sample email (emailSample1.txt)\n');

% Extract Features
file_contents = readFile('emailSample1.txt');
word_indices  = processEmail(file_contents);

% Print Stats
fprintf('Word Indices: \n');
fprintf(' %d', word_indices);
fprintf('\n\n');

fprintf('Program paused. Press enter to continue.\n');
pause;

%% ==================== Part 2: Feature Extraction ====================
%  Now, you will convert each email into a vector of features in R^n.
%  You should complete the code in emailFeatures.m to produce a feature
%  vector for a given email.

fprintf('\nExtracting features from sample email (emailSample1.txt)\n');

% Extract Features
file_contents = readFile('emailSample1.txt');
word_indices  = processEmail(file_contents);
features      = emailFeatures(word_indices);

% Print Stats
fprintf('Length of feature vector: %d\n', length(features));
fprintf('Number of non-zero entries: %d\n', sum(features > 0));

fprintf('Program paused. Press enter to continue.\n');
pause;

%% =========== Part 3: Train Linear SVM for Spam Classification ========
%  In this section, you will train a linear classifier to determine if an
%  email is Spam or Not-Spam.

% Load the Spam Email dataset
% You will have X, y in your environment
load('spamTrain.mat');

fprintf('\nTraining Linear SVM (Spam Classification)\n')
fprintf('(this may take 1 to 2 minutes) ...\n')

C = 0.1;
model = svmTrain(X, y, C, @linearKernel);

p = svmPredict(model, X);

fprintf('Training Accuracy: %f\n', mean(double(p == y)) * 100);

%% =================== Part 4: Test Spam Classification ================
%  After training the classifier, we can evaluate it on a test set. We have
%  included a test set in spamTest.mat

% Load the test dataset
% You will have Xtest, ytest in your environment
load('spamTest.mat');

fprintf('\nEvaluating the trained Linear SVM on a test set ...\n')

p = svmPredict(model, Xtest);

fprintf('Test Accuracy: %f\n', mean(double(p == ytest)) * 100);
pause;

%% ================= Part 5: Top Predictors of Spam ====================
%  Since the model we are training is a linear SVM, we can inspect the
%  weights learned by the model to understand better how it is determining
%  whether an email is spam or not. The following code finds the words with
%  the highest weights in the classifier. Informally, the classifier
%  'thinks' that these words are the most likely indicators of spam.
%

% Sort the weights and obtin the vocabulary list
[weight, idx] = sort(model.w, 'descend');
vocabList = getVocabList();

fprintf('\nTop predictors of spam: \n');
for i = 1:15
    fprintf(' %-15s (%f) \n', vocabList{idx(i)}, weight(i));
end

fprintf('\n\n');
fprintf('\nProgram paused. Press enter to continue.\n');
pause;

%% =================== Part 6: Try Your Own Emails =====================
%  Now that you've trained the spam classifier, you can use it on your own
%  emails! In the starter code, we have included spamSample1.txt,
%  spamSample2.txt, emailSample1.txt and emailSample2.txt as examples.
%  The following code reads in one of these emails and then uses your
%  learned SVM classifier to determine whether the email is Spam or
%  Not Spam

% Set the file to be read in (change this to spamSample2.txt,
% emailSample1.txt or emailSample2.txt to see different predictions on
% different emails types). Try your own emails as well!
filename = 'spamSample1.txt';

% Read and predict
file_contents = readFile(filename);
word_indices  = processEmail(file_contents);
x             = emailFeatures(word_indices);
p = svmPredict(model, x);

fprintf('\nProcessed %s\n\nSpam Classification: %d\n', filename, p);
fprintf('(1 indicates spam, 0 indicates not spam)\n\n');


% Digital Video Stabilization and Rolling Shutter Correction using Gyroscopes
% Copyright (C) 2011 Alexandre Karpenko
%
% 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 3 of the License, or
% 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/>.

function new_theta = integrate_and_resample(w, old_time, new_time)

dT = old_time(2:end) - old_time(1:end-1);
theta = cumsum( w(2:end) .* dT );

new_theta = interp1(old_time(2:end), theta(:,1), new_time);
new_theta(isnan(new_theta)) = 0;


%
% Call the Fit() method of the model to fit
%
function AMICO_Fit()

	global CONFIG niiSIGNAL niiMASK bMATRIX

	% Fit right model to each voxel
    % =============================
	if ~isempty(CONFIG.model)

        % fit the model to the data
        fprintf( '\n-> Fitting "%s" model to %d voxels:\n', CONFIG.model.name, nnz(niiMASK.img) );
        TIME = tic;
        progress = ProgressBar( nnz(niiMASK.img) );
        for iz = 1:niiSIGNAL.hdr.dime.dim(4)
        for iy = 1:niiSIGNAL.hdr.dime.dim(3)
        for ix = 1:niiSIGNAL.hdr.dime.dim(2)
            if niiMASK.img(ix,iy,iz)==0, continue, end
            progress.update();

            try
                % Read the signal
                b0 = mean( squeeze( niiSIGNAL.img(ix,iy,iz,CONFIG.scheme.b0_idx) ) );
                if ( b0 < 1e-3 ), continue, end
                y = double( squeeze( niiSIGNAL.img(ix,iy,iz,:) ) ./ ( b0 + eps ) );
                y( y < 0 ) = 0; % [NOTE] this should not happen!

                % Find the MAIN DIFFUSION DIRECTIONS
                if any(strcmp(properties(CONFIG.model), 'max_dirs'))==false || CONFIG.model.max_dirs>0
                    % using DTI
                    [ ~, ~, V ] = AMICO_FitTensor( y, bMATRIX );
                    vox_DIRs = V(:,1);
                    if ( vox_DIRs(2)<0 ), vox_DIRs = -vox_DIRs; end
                    [ i1, i2 ] = AMICO_Dir2idx( vox_DIRs );
                    DIRs(ix,iy,iz,:) = vox_DIRs;
                else
                    % not needed by the model
                    DIRs(ix,iy,iz,:) = 0;
                    i1 = 1;
                    i2 = 1;
                end

                % Dispatch to the right handler for each model
                vox_MAPs = CONFIG.model.Fit( y, i1, i2 );

                % Store results
                MAPs(ix,iy,iz,:) = vox_MAPs;
            catch exception
                % set output maps to NaN in case of problems
                MAPs(ix,iy,iz,:) = NaN;
            end
        end
        end
        end
        progress.close();

        TIME = toc(TIME);
        fprintf( '   [ %.0fh %.0fm %.0fs ]\n', floor(TIME/3600), floor(mod(TIME/60,60)), mod(TIME,60) )
		CONFIG.OPTIMIZATION.fit_time = TIME;
	else
		error( '[AMICO_Fit] Model not set' )
    end

	% Save CONFIGURATION and OUTPUT to file
    % =====================================
    fprintf( '\n-> Saving output to "AMICO/*":\n' );

    fprintf( '\t- CONFIG.mat' );
    save( fullfile(CONFIG.OUTPUT_path,'CONFIG.mat'), '-v6', 'CONFIG' )
    fprintf( ' [OK]\n' );

    fprintf( '\t- FIT_dir.nii' );
    niiMAP = niiMASK;
    niiMAP.hdr.dime.dim(1) = 4;
    niiMAP.hdr.dime.datatype = 16;
    niiMAP.hdr.dime.bitpix = 32;
    niiMAP.hdr.dime.glmin = -1;
    niiMAP.hdr.dime.glmax = 1;
    niiMAP.hdr.dime.calmin = niiMAP.hdr.dime.glmin;
    niiMAP.hdr.dime.calmax = niiMAP.hdr.dime.glmax;
    niiMAP.hdr.dime.scl_slope = 1;
    niiMAP.hdr.dime.scl_inter = 0;

    niiMAP.img = DIRs;
    niiMAP.hdr.dime.dim(5) = size(DIRs,4);
    save_untouch_nii( niiMAP, fullfile(CONFIG.OUTPUT_path,'FIT_dir.nii') );
    fprintf( ' [OK]\n' );

    niiMAP.hdr.dime.dim(5) = 1;
    for i = 1:numel(CONFIG.model.OUTPUT_names)
        fprintf( '\t- AMICO/FIT_%s.nii', CONFIG.model.OUTPUT_names{i} );

        niiMAP.img = MAPs(:,:,:,i);
        niiMAP.hdr.hist.descrip = CONFIG.model.OUTPUT_descriptions{i};
        niiMAP.hdr.dime.glmin = min(niiMAP.img(:));
        niiMAP.hdr.dime.glmax = max(niiMAP.img(:));
        niiMAP.hdr.dime.calmin = niiMAP.hdr.dime.glmin;
        niiMAP.hdr.dime.calmax = niiMAP.hdr.dime.glmax;
        save_untouch_nii( niiMAP, fullfile(CONFIG.OUTPUT_path,['FIT_' CONFIG.model.OUTPUT_names{i} '.nii']) );

        fprintf( ' [OK]\n' );
    end

    fprintf( '   [ DONE ]\n' )
end


function boundary = p11_boundary_nearest ( m, n, point )

%*****************************************************************************80
%
%% P11_BOUNDARY_NEAREST returns a nearest boundary point in problem 11.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    12 April 2009
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer M, the spatial dimension.
%
%    Input, integer N, the number of points.
%
%    Input, real POINT(M,N), the coordinates of the points.
%
%    Output, real BOUNDARY(M,N), points on the boundary
%    that are nearest to each point.
%
  q1 = [ ...
    0.00, 0.00; ...
    1.00, 0.00; ...
    0.75, 0.25; ...
    0.25, 0.25 ]';
  q2 = [ ...
    0.00, 0.00; ...
    0.25, 0.25; ...
    0.25, 0.75; ...
    0.00, 1.00 ]';
  q3 = [ ...
    0.50, 0.50; ...
    0.50, 0.25; ...
    0.75, 0.25; ...
    1.00, 0.50 ]';
  q4 = [ ...
    0.50, 0.50; ...
    0.25, 0.50; ...
    0.25, 0.25; ...
    0.50, 0.25 ]';
  q5 = [ ...
    0.50, 0.50; ...
    0.50, 1.00; ...
    0.25, 0.75; ...
    0.25, 0.50 ]';
  t1 = [ ...
    1.00, 0.00; ...
    1.00, 0.50; ...
    0.75, 0.25 ]';
  t2 = [ ...
    0.00, 1.00; ...
    0.25, 0.25; ...
    0.50, 1.00 ]';
  t3 = [ ...
    0.50, 0.50; ...
    1.00, 0.50; ...
    1.00, 1.00 ]';
  t4 = [ ...
    0.50, 0.50; ...
    1.00, 1.00; ...
    0.50, 1.00 ]';

  x1 =  0.0;
  x2 =  0.5;
  x3 = +1.0;
  y1 =  0.0;
  y2 =  0.5;
  y3 = +1.0;

  for j = 1 : n

    if ( point(1,j) <= x1 & point(2,j) <= y1 )

      boundary(1,j) = x1;
      boundary(2,j) = y1;

    elseif ( point(1,j) <= x1 & point(2,j) <= y3 )

      boundary(1,j) = x1;
      boundary(2,j) = point(2,j);

    elseif ( point(1,j) <= x1 & y3 <= point(2,j) )

      boundary(1,j) = x1;
      boundary(2,j) = y3;

    elseif ( point(1,j) <= x3 & point(2,j) <= y1 )

      boundary(1,j) = point(1,j);
      boundary(2,j) = y1;

    elseif ( quad_contains_point_2d ( q1, point(1:2,j) ) )

      boundary(1,j) = point(1,j);
      boundary(2,j) = y1;

    elseif ( quad_contains_point_2d ( q2, point(1:2,j) ) )

      boundary(1,j) = x1;
      boundary(2,j) = point(2,j);

    elseif ( triangle_contains_point_2d ( t1, point(1:2,j) ) )

      boundary(1,j) = x3;
      boundary(2,j) = point(2,j);

    elseif ( quad_contains_point_2d ( q3, point(1:2,j) ) )

      boundary(1,j) = point(1,j);
      boundary(2,j) = y2;

    elseif ( quad_contains_point_2d ( q4, point(1:2,j) ) )

      boundary(1,j) = x2;
      boundary(2,j) = y2;

    elseif ( quad_contains_point_2d ( q5, point(1:2,j) ) )

      boundary(1,j) = x2;
      boundary(2,j) = point(2,j);

    elseif ( triangle_contains_point_2d ( t2, point(1:2,j) ) )

      boundary(1,j) = point(1,j);
      boundary(2,j) = y3;

    elseif ( x1 <= point(1,j) & point(1,j) <= x2 & y3 <= point(2,j) )

      boundary(1,j) = point(1,j);
      boundary(2,j) = y3;

    elseif ( triangle_contains_point_2d ( t3, point(1:2,j) ) )

      boundary(1,j) = point(1,j);
      boundary(2,j) = y2;

    elseif ( triangle_contains_point_2d ( t4, point(1:2,j) ) )

      boundary(1,j) = x2;
      boundary(2,j) = point(2,j);

    elseif ( x2 <= point(1,j) & point(1,j) <= x3 & y3 <= point(2,j) )

      boundary(1,j) = x2;
      boundary(2,j) = y3;

    elseif ( x3 <= point(1,j) & point(2,j) <= y1 )

      boundary(1,j) = x3;
      boundary(2,j) = y1;

    elseif ( x3 <= point(1,j) & y1 <= point(2,j) & point(2,j) <= y2 )

      boundary(1,j) = x3;
      boundary(2,j) = point(2,j);

    elseif ( x3 <= point(1,j) & y2 <= point(2,j) & point(2,j) <= y3 )

      boundary(1,j) = x3;
      boundary(2,j) = y2;

    elseif ( x3 <= point(1,j) & y3 <= point(2,j) & point(2,j) <= point(1,j) )

      boundary(1,j) = x3;
      boundary(2,j) = y2;

    elseif ( x3 <= point(1,j) & y3 <= point(2,j) & point(1,j) <= point(2,j) )

      boundary(1,j) = x2;
      boundary(2,j) = y3;

    else

      fprintf ( 1, '\n' );
      fprintf ( 1, 'P11_BOUNDARY_NEAREST - Fatal error!\n' );
      fprintf ( 1, '  Orphan point = ( %f, %f )\n', point(1:2,j) );
      error ( 'P11_BOUNDARY_NEAREST - Fatal error!' );

    end

  end

  return
end


% LIONSAMBA example script
% Continuous custom current profile: this script shows how to run a continuous custom current
% profile charge/discharge.

%   This file is part of the LIONSIMBA Toolbox
%
%	Official web-site: 	http://sisdin.unipv.it/labsisdin/lionsimba.php
% 	Official GitHUB: 	https://github.com/lionsimbatoolbox/LIONSIMBA
%
%   LIONSIMBA: A Matlab framework based on a finite volume model suitable for Li-ion battery design, simulation, and control
%   Copyright (C) 2016-2018 :Marcello Torchio, Lalo Magni, Davide Raimondo,
%                            University of Pavia, 27100, Pavia, Italy
%                            Bhushan Gopaluni, Univ. of British Columbia,
%                            Vancouver, BC V6T 1Z3, Canada
%                            Richard D. Braatz,
%                            Massachusetts Institute of Technology,
%                            Cambridge, Massachusetts 02142, USA
%
%   Main code contributors to LIONSIMBA 2.0:
%                           Ian Campbell, Krishnakumar Gopalakrishnan,
%                           Imperial college London, London, UK
%
%   LIONSIMBA is a free Matlab-based software distributed with an MIT
%   license.

% Clear the workspace
clear
close all

%% Parameters
% Define the integration times.
t0 = 0;
tf = 10^4;

% Define the initial state structure
initialState.Y  = [];
initialState.YP = [];

% Define the parameters structure.
param{1}               = Parameters_init;

param{1}.Np            = 10;
param{1}.Ns            = 10;
param{1}.Nn            = 10;

param{1}.hcell         = 1;
param{1}.Tref          = 298.15;

param{1}.AbsTol        = 1e-6;
param{1}.RelTol        = 1e-6;

param{1}.CutoffSOC     = 20;

param{1}.SolidPhaseDiffusion = 1;

if(param{1}.SolidPhaseDiffusion == 3)
    multp = param{1}.Nr_p;
    multn = param{1}.Nr_n;
else
    multp = 1;
    multn = 1;
end
I1C = 29.5;

C_rate = 1.5;
%% Discharge section
% Discharge the battery to the 20% of SOC
out = startSimulation(t0,tf,initialState,-25,param);

% Store the Jacobian matrix for future computations. This is possible
% because the different scenarios share the same model structure, and the
% only quantity which differs is the applied current density.
param{1}.JacobianFunction = out.JacobianFun;

% Update the initial states
initialState = out.initialState;

% Redefine the cutoff SOC in order to avoid simulation interruptions
param{1}.CutoffSOC     = 2;

% Rest the battery with no current applied
out2 = startSimulation(0,5000,initialState,0,param);

% Update the initial states
initialState = out2.initialState;

% Apply a custom current profile
param{1}.OperatingMode = 4;

% Define which external function is going to be used from the simulato to
% gather the value of the applied current density at each time step
param{1}.CurrentDensityFunction = @getInputCurrent;

% Run the simulation
out3 = startSimulation(0,5000,initialState,0,param);

% Update the initial states
initialState = out3.initialState;

% Go back to galvanostatic operating conditions
param{1}.OperatingMode= 1;

% Run the simulation
out4 = startSimulation(0,5000,initialState,0,param);
%% Plot the results

% Concatenate all the time results
time = [out.time{1};out2.time{1}+out.time{1}(end);out3.time{1}+out2.time{1}(end)+out.time{1}(end);out4.time{1}+out3.time{1}(end)+out2.time{1}(end)+out.time{1}(end)];

figure(1)
plot(time,[out.Voltage{1};out2.Voltage{1};out3.Voltage{1};out4.Voltage{1}],'LineWidth',6)
hold on
xlabel('Time [s]')
ylabel('Voltage [V]')
grid on
box on
title('Cell Voltage')

figure(2)
plot(time,[out.SOC{1};out2.SOC{1};out3.SOC{1};out4.SOC{1}],'LineWidth',6)
hold on
xlabel('Time [s]')
ylabel('SOC [%]')
grid on
box on
title('Cell SOC')

figure(3)
plot(time,[out.Temperature{1}(:,end);out2.Temperature{1}(:,end);out3.Temperature{1}(:,end);out4.Temperature{1}(:,end)],'LineWidth',6)
hold on
xlabel('Time [s]')
ylabel('Temperature[K]')
grid on
box on
title('Cell Temperature')

figure(4)
plot(time,[out.curr_density;out2.curr_density;out3.curr_density;out4.curr_density],'LineWidth',6)
hold on
xlabel('Time [s]')
ylabel('Applied Current [A/m^2]')
grid on
box on
title('Cell Input Current')

figure(5)
plot(time,[out.cs_average{1}(:,[1 param{1}.Np*multp]);out2.cs_average{1}(:,[1 param{1}.Np*multp]);out3.cs_average{1}(:,[1 param{1}.Np*multp]);out4.cs_average{1}(:,[1 param{1}.Np*multp])],'LineWidth',6)
hold on
line([time(1) time(end)],[param{1}.cs_maxp param{1}.cs_maxp],'LineWidth',6,'LineStyle','--')
xlabel('Time [s]')
ylabel('Positive electrode average concentration [mol/m^3]')
grid on
box on
legend('c^* x=0','c^* x=l_p','c^* max')

figure(6)
plot(time,[out.cs_average{1}(:,[(param{1}.Np+1)*multn (param{1}.Np+param{1}.Nn)*multn]);out2.cs_average{1}(:,[param{1}.Np+1 param{1}.Np+param{1}.Nn]*multn);out3.cs_average{1}(:,[param{1}.Np+1 param{1}.Np+param{1}.Nn]*multn);out4.cs_average{1}(:,[param{1}.Np+1 param{1}.Np+param{1}.Nn]*multn)],'LineWidth',6)
hold on
line([time(1) time(end)],[param{1}.cs_maxn param{1}.cs_maxn],'LineWidth',6,'LineStyle','--')
xlabel('Time [s]')
ylabel('Negative electrode average concentration [mol/m^3]')
grid on
box on
legend('c^* x=l_p+l_s','c^* x=l_p+l_s+l_n','c^* max')


function [high_fissure_indices, ref_image] = PTKGetMaxFissurePoints(fissure_approximation, lung_mask, fissureness, image_roi, image_size)
    % PTKGetMaxFissurePoints. function for finding candidate points of high
    %     fissureness given an initial fissure approximation.
    %
    %     PTKGetMaxFissurePoints is an intermediate stage in segmenting the
    %     lobes. It is not intended to be a general-purpose algorithm.
    %
    %     For more information, see
    %     [Doel et al., Pulmonary lobe segmentation from CT images using
    %     fissureness, airways, vessels and multilevel B-splines, 2012]
    %
    %     Licence
    %     -------
    %     Part of the TD Pulmonary Toolkit. https://github.com/tomdoel/pulmonarytoolkit
    %     Author: Tom Doel, 2012.  www.tomdoel.com
    %     Distributed under the GNU GPL v3 licence. Please see website for details.
    %

    data_points_indices = find(fissure_approximation);

    bin_size = 2;

    [high_fissure_indices, ref_image] = GetFissurePoints(data_points_indices, image_size, lung_mask, fissureness, image_roi, bin_size);
end

function [maximum_fissureness_indices, ref_image] = GetFissurePoints(indices_for_model, image_size, lung_segmentation, fissureness, image_roi, bin_size)

    % Get the coordinates of every voxel in the lung segmentation
    candidate_indices = find(lung_segmentation.RawImage);

    % An image for debugging
    ref_image = zeros(image_size, 'uint8');
    ref_image(candidate_indices) = 2;

    % Remove points with low fissureness
    max_fissureness = max(fissureness.RawImage(candidate_indices));
    fissureness_threshold = max_fissureness/3;
    intensity_threshold_hu = -900;
    intensity_threshold = image_roi.HounsfieldToGreyscale(intensity_threshold_hu);
    candidate_indices = candidate_indices((fissureness.RawImage(candidate_indices) > fissureness_threshold) & (image_roi.RawImage(candidate_indices) > intensity_threshold));
    ref_image(candidate_indices) = 4;

    if isempty(candidate_indices)
        maximum_fissureness_indices = [];
        return;
    end

    % Find a rotation matrix
    [eigv, m] = GetRotationMatrix(indices_for_model, image_size);

    [x_all, y_all, z_all] = MimImageCoordinateUtilities.FastInd2sub(image_size, candidate_indices);
    X_all = [x_all(:), y_all(:), z_all(:)]';

    % Transform to new basis
    em_all = X_all - m(:, ones(1, size(X_all, 2)));
    em_all = eigv*em_all;

    % We allocate each point to a bin according to the x-y coordinates in the
    % transformed domain
    x1_all = em_all(1, :);
    y1_all = em_all(2, :);

    % Get the coordinates of each of the model points
    [x_model, y_model, z_model] = MimImageCoordinateUtilities.FastInd2sub(image_size, indices_for_model);
    X_model = [x_model(:), y_model(:), z_model(:)]';

    % Transform to new basis
    em_model = X_model - m(:, ones(1, size(X_model, 2)));
    em_model = eigv*em_model;

    % We allocate each point to a bin according to the x-y coordinates in the
    % transformed domain
    x1_model = em_model(1, :);
    y1_model = em_model(2, :);

    % Project the candidate points onto the fissure plane, and remove those that
    % are not within the convex hull formed by the model points on the plane. In
    % effect, we are using the model coordinates (the initial guess) as a bound
    % for the x-y coordinates used to construct the fissure surface.
    xy_coords_model = [x1_model', y1_model'];
    dt = DelaunayTri(xy_coords_model);
    simplex_index = pointLocation(dt, x1_all', y1_all');
    is_valid = ~isnan(simplex_index);
    candidate_indices_ok = candidate_indices(is_valid);

    if isempty(candidate_indices_ok)
        maximum_fissureness_indices = [];
        return;
    end

    % Turn into x,y,z vectors
    [x_all, y_all, z_all] = MimImageCoordinateUtilities.FastInd2sub(image_size, candidate_indices_ok);
    X_all = [x_all(:), y_all(:), z_all(:)]';

    % Transform to new basis
    em_all = X_all - m(:, ones(1, size(X_all, 2)));
    em_all = eigv*em_all;

    % We allocate each point to a bin according to the x-y coordinates in the
    % transformed domain
    x1_all = em_all(1, :);
    y1_all = em_all(2, :);

    [maximum_fissureness_indices, all_maxima] = SortIntoBins(x1_all, y1_all, candidate_indices_ok, fissureness, bin_size, image_size);

    ref_image(all_maxima) = 6;
    ref_image(maximum_fissureness_indices) = 3;

    % Remove non-connected points (outliers)
    [x_all, y_all, z_all] = MimImageCoordinateUtilities.FastInd2sub(image_size, maximum_fissureness_indices);
    points_coords = ArraysToPoints(x_all(:), y_all(:), z_all(:));

    min_dilate_size_mm = 2.5;
    max_dilate_size_mm = 2.5;

    dilate_size_mm = min_dilate_size_mm;

    calculate_again = true;
    target_number_of_points = round(numel(x_all)/2);

    % Perform the removal of connected points with a variable dilation size
    while calculate_again
        points_coords_new = RemoveNonConnectedPoints(points_coords, lung_segmentation, image_size, dilate_size_mm);
        number_of_found_points = numel(points_coords_new);
        if (number_of_found_points >= target_number_of_points) || (dilate_size_mm > max_dilate_size_mm)
            calculate_again = false;
        else
            dilate_size_mm = dilate_size_mm + 0.5;
        end

    end

    [x_all, y_all, z_all] = PointsToArrays(points_coords_new);
    maximum_fissureness_indices = MimImageCoordinateUtilities.FastSub2ind(image_size, x_all(:), y_all(:), z_all(:));
    ref_image(maximum_fissureness_indices) = 1;

end

function [rot_matrix, m] = GetRotationMatrix(indices, image_size)
    [x, y, z] = ind2sub(image_size, indices);
    X = [x, y, z]';

    % Find a suitable basis for these points using PCA
    m = mean(X, 2);
    em = X - m(:, ones(1, size(X, 2)));
    eigv = pts_pca(em);

    rot_matrix = eigv';
end

function [maximum_fissureness_indices, all_maxima] = SortIntoBins(x1, y1, candidate_indices, fissureness, bin_size, image_size)
    binx = floor(x1/bin_size);
    binx = binx - min(binx);
    biny = floor(y1/bin_size);
    biny = biny - min(biny);

    % bin is an array containing the bin to which each element of indices has been allocated
    bin = binx + (max(binx)+1)*biny;

    % Now sort the indices and bins by fissureness
    fissureness_at_indices = fissureness.RawImage(candidate_indices);

    is_maxima = GetMaxima(candidate_indices, bin, fissureness_at_indices, fissureness, image_size);
    all_maxima = candidate_indices(is_maxima);
    bin = bin(is_maxima);
    candidate_indices = candidate_indices(is_maxima);

    fissureness_at_indices = fissureness.RawImage(candidate_indices);

    [~, sorted_indices] = sort(fissureness_at_indices, 'descend');
    indices_sorted_by_fissureness = candidate_indices(sorted_indices);
    bins_sorted_by_fissureness = bin(sorted_indices);

    % Use unique to obtain the first indices corresponding to each bin - this
    % will be the point with the largest fissureness
    maximum_fissureness_indices = [];
    for selection_index = 1 : 1
        [~, bin_indices, ~] = unique(bins_sorted_by_fissureness, 'first');
        maximum_fissureness_indices = [maximum_fissureness_indices; indices_sorted_by_fissureness(bin_indices)];

        bins_sorted_by_fissureness(bin_indices) = [];
        indices_sorted_by_fissureness(bin_indices) = [];
    end

    maximum_fissureness_indices = maximum_fissureness_indices(fissureness.RawImage(maximum_fissureness_indices) > 0);

end

function is_maxima = GetMaxima(candidate_indices, bin, fissureness_at_indices, fissureness, image_size)
    bin_allocation = zeros(image_size, 'int32');
    bin_allocation(candidate_indices) = bin;

    [linear_offsets, ~] = MimImageCoordinateUtilities.GetLinearOffsets(image_size);
    neighbours = repmat(int32(candidate_indices), 1, 6) + repmat(int32(linear_offsets), length(candidate_indices), 1);
    fissureness_neighbours = fissureness.RawImage(neighbours);
    bins_neighbours = bin_allocation(neighbours);
    bins_match = bins_neighbours == repmat(bin', 1, 6);
    fissureness_centre = repmat(fissureness_at_indices, 1, 6);

    % Force neighbouring points in different bins to have a lower fissureness
    fissureness_neighbours(~bins_match) = fissureness_centre(~bins_match) - 1;
    fissureness_less = fissureness_neighbours < fissureness_centre;
    sums = sum(fissureness_less, 2);
    is_maxima = sums == 6;
end

function points_coords = RemoveNonConnectedPoints(points_coords, template_image, image_size, dilate_size_mm)
    voxel_volume = prod(template_image.VoxelSize);
    min_component_size_mm3 = 300;
    min_component_size_voxels = round(min_component_size_mm3/voxel_volume);

    image_mask = false(image_size);
    indices = sub2ind(image_size, points_coords(:,1), points_coords(:,2), points_coords(:,3));
    image_mask(indices) = true;

    tci = template_image.BlankCopy;
    tci.ChangeRawImage(image_mask);
    tci.BinaryMorph(@imdilate, dilate_size_mm);
    connected_image = tci.RawImage;

    connected_components = bwconncomp(connected_image, 26);
    num_components = connected_components.NumObjects;
    for component = 1 : num_components
        pixels = connected_components.PixelIdxList{component};
        if length(pixels) < min_component_size_voxels
            connected_image(pixels) = false;
        end
    end
    image_mask = image_mask & connected_image;
    indices = find(image_mask(:));
    [i2_coords, j2_coords, k2_coords] = ind2sub(image_size, indices);
    points_coords = ArraysToPoints(i2_coords, j2_coords, k2_coords);
end

function points = ArraysToPoints(i, j, k)
    points = [i(:), j(:), k(:), ones(size(i(:)))];
end

function [is, js, ks] = PointsToArrays(points)
    is = points(:, 1);
    js = points(:, 2);
    ks = points(:, 3);
end


% compute the source zones for landers

report_this_filefun(mfilename('fullpath'));

% compute the source zones for landers

% define Input parameters
if ~exist('nb', 'var'); nb = 10; end
if ~exist('tmin1', 'var'); tmin1 = 0.05; end
if ~exist('tpre', 'var'); tpre = 30; end
if ~exist('Tobs', 'var'); Tobs = 30; end
if ~exist('prol', 'var'); prol = 0.5; end
if isempty(storedcat); storedcat=a; end

def = {num2str(nb),num2str(tmin1),num2str(tpre),num2str(Tobs), num2str(prol)};

tit ='Hazard analysis input parameters';
prompt={ 'Number of source zones',...
    'Minimum Time (tmin1) [days]',...
    'Prediction Time (tpre) [days]',...
    'Data period considered (Tobs) [days] ',...
    'Probability level (prol)',...
    };

ni2 = inputdlg(prompt,tit,1,def);

l = ni2{1}; nb= str2double(l);
l = ni2{2}; tmin1= str2double(l);
l = ni2{3}; tpre= str2double(l);
l = ni2{4}; Tobs = str2double(l);
l = ni2{5}; prol= str2double(l);

tlen = Tobs;
l=storedcat.Date < min(maepi.Date) + days(Tobs);
a=storedcat.subset(l);
da = []; anz = [];
B = [];
dt = 1;
update(mainmap())

cd /home2/stefan/ZMAP/aspar
fid2 = fopen('sourczones.txt','w');

l1 = [];

for kk = 1:nb-1
    l1 = [l1 prctile2(a.Latitude,kk*100/nb)];
end

bo = [34.35 l1 34.88 ];
for ii = 1:length(bo)-1
    l = a.Latitude >= bo(ii) & a.Latitude < bo(ii+1);
    figure_w_normalized_uicontrolunits(map)
    hold on
    plot(a(l,1),a(l,2),'k+')

    b1 = prctile2(a(l,1),25);
    b2 = prctile2(a(l,1),75);
    plot(b1,bo(ii),'+r','Markersize',12)
    plot(b2,bo(ii),'+r','Markersize',12)
    plot(b1,bo(ii+1),'+r','Markersize',12)
    plot(b2,bo(ii+1),'+r','Markersize',12)
    drawnow

    newt2 = a.subset(l);
    calcp
    B = [B ; b1 bo(ii)  b2 bo(ii)];

    anz = [];
    for m2 = 4.25:0.5:6.25
        M2 = maepi(1,6) - m2;
        t0 = days(max(a.Date) - min(maepi));
        pla = 0; pla2 = 0;

        for t = t0:dt:t0+tpre
            pla = pla + (10^(A + bv*(M2)) * (t + c)^(-p))  *dt;
            pla2 = pla2 + (10^(A + bv*(M2-0.5)) * (t + c)^(-p))  *dt;
        end

        %fac = length(a(l,1))/a.Count
        anz = [anz ;  m2+0.25  (pla-pla2)/tpre];

    end

    % write info to file
    s = ['0    1.     -1          zn035.00']; s = s';
    fprintf(fid2,'%s\n',s);
    s = ['2 1 1']; s = s';
    fprintf(fid2,'%s\n',s);
    s = [num2str(-b1,5) ' ' num2str(bo(ii)) ' ' num2str(-b2,5) ' ' num2str(bo(ii))]; s = s';
    fprintf(fid2,'%s\n',s);
    s = [num2str(-b1,5) ' ' num2str(bo(ii+1)) ' ' num2str(-b2,5) ' ' num2str(bo(ii+1))]; s = s';
    fprintf(fid2,'%s\n',s);
    fprintf(fid2,'%7.6f    ',anz(:,2));
    fprintf(fid2,'\n');
    fprintf(fid2,'\n');
    fprintf(fid2,'%3.2f  ',anz(:,1));
    fprintf(fid2,'\n');
end

fclose(fid2)

%return
cd /home2/stefan/ZMAP/aspar
do = [' ! cat head5.txt | sed -e"s/sub1/' num2str(prol) ' 1 ' num2str(tpre) '/" > head2.txt ' ]; eval(do)
do = [' ! cat head2.txt sourczones.txt tail.txt > /home2/stefan/srisk/myrisk.inp']; eval(do)
cd /home2/stefan/srisk/

do = [ '! /nfs/alaska/home2/stefan/srisk/seis4b.exe  myrisk.inp myrisk.out f2 f3' ]; eval(do)
do = [' !cat myrisk.out | grep -e "LAT   "  -e "' num2str(tpre) ' YE"  > tmp2 ']; eval(do)
try
    condata2
catch ME
    error_handler(ME, @do_nothing);
end

[X2,Y2] = meshgrid((-116.8:0.02:-115.7),(33.92:0.01:35.3));

Z = griddata(-da(:,1),da(:,2),da(:,3),X2,Y2,'linear');
figure

axes('pos',[0.05 0.1 0.4 0.7])
pcolor(X2,Y2,Z);
set(gca,'TickDir','out');
colorbar; shading flat
hold on
shading interp
overlay
[ca1, ca2] = caxis;

cd /home2/stefan/ZMAP

mati = decyear(min(maepi.Date));
l = obs(:,3) > mati + tlen/365 &  obs(:,3) <  mati+ tlen/365 + tpre/365;
b = obs(l,:);
l = b(:,6) > 3;
b = b(l,:);

ve = [];dx = 0.02; dy = 0.02

for x = -116.8:dx:-115.7
    for y = 33.92:dy:35.3
        ve =    [ ve ; x y ];
    end
end

le = length(ve);
Y0 = zeros(le,1);

for i = 1:length(b)
    di2 = deg2km((distance(ve(:,2),ve(:,1),repmat(b(i,2),le,1),repmat(b(i,1),le,1))));
    R = di2;
    r = sqrt(R.^2 + 5.57^2);
    M = b(i,6);
    Y = -0.136 + 0.229*(M-6) - 0.778 * log10(r) + 0.161*1 ;
    Y = 10.^Y;
    c = [Y , Y0];
    mapga = max(c');
    Y0 = mapga';
end

mapga = mapga';

l1 = length(-116.8:dx:-115.7);
l2 = length(33.92:dy:35.3);

axes('pos',[0.55 0.1 0.4 0.7])
re = reshape(mapga,l2,l1);
rey = reshape(ve(:,2),l2,l1);
rex = reshape(ve(:,1),l2,l1);
pcolor(rex,rey,re);
set(gca,'YTicklabels',[],'TickDir','out');
caxis([ca1 ca2]);
shading interp

colorbar;
overlay
hold on


filename='Gripping_tetrahedra_coarse';
 ptype = 'MACRO';
method = 'SIMPALL';
materialType = 'ISOTROPIC';
initial_case = 'full';
cost = {'nonadjoint_compliance'};
weights = [1, 0.1];
constraint = {'volumeConstraint'};
constraint_case = 'INEQUALITY';
optimizer = 'MMA'; incrementFactor = 1;
filterType = 'P1';

nsteps = 1;
Vfrac_final = 0.4;
optimality_final = 1e-3;
constr_final =1e-3;

Vfrac_initial = 1;
optimality_initial = 1e-3;
constr_initial = 1e-3;
TOL.rho_plus = 1;
TOL.rho_minus = 0;
TOL.E_plus = 1;
TOL.E_minus = 1e-3;
TOL.nu_plus = 1/3;
TOL.nu_minus = 1/3;


function plotAbsolute(simOutputArray, Xref)
% plotAbsolute Plot absolute position and velocity from simulation output
%       This function plots the absolute position and velocity of the ACC
%       vehicle as well as the tracking vehicle for all the simulation
%       cases.
%
% Author : Ajinkya Khade, askhade@ncsu.edu

% List of y-axis labels for the 2 subplots
ylabelArray = {'Absolute Position (m)', 'Absolute Velocity (m/s)'};

% List of titles for the 2 subplots
titleArray  = {'(a)', '(b)', '(c)', '(d)'};

% List of line colors to be used for the different simulation cases
lineColorArray = {'k', 'b', 'r', 'm'};

% initializing array to store plot handles
h = zeros(2, length(simOutputArray));

% empty cell array to store legend entries
legendLog = {};

figure('units','normalized','outerposition',[0 0 1 1])

% plotting data for each simulation object stored in array
for i = 1:length(simOutputArray)

    tplot = 0:simOutputArray(i).ts:simOutputArray(i).tsim;              % Array containing simulation time steps
    Nsim = floor(simOutputArray(i).tsim/simOutputArray(i).ts) + 1;      % Number of time steps in simulation

    % calculating absolute position and velocity
    yplot = simOutputArray(i).empc(1:2,:) + Xref(1:2,1:end-1);
    yplot(1,:) = yplot(1,:) - simOutputArray(i).sivd;

    % Searching for point where relative distance becomes
    % positive, indicating a collision
    tcInd = find((simOutputArray(i).empc(1,:) - simOutputArray(i).sivd) > 0.0001, 1);

    % Setting point of collision to end point - this is just to prevent
    % errors because of the way the code works
    if isempty(tcInd)
        tcInd = Nsim;
    end

    % plotting position and velocity on different subplots
    for subplotNum = 1:2
        subplot(2,1,subplotNum)

        hold on; grid on;

        h(subplotNum,2*i-1) = plot(tplot(1:tcInd-1), yplot(subplotNum,1:tcInd-1),'LineStyle', '-', 'Color', lineColorArray{i});
        h(subplotNum,2*i)   = plot(tplot(1:tcInd-1), Xref(subplotNum,1:tcInd-1),'LineStyle', '--', 'Color', lineColorArray{i});

        % if collision is detected, use different line styles before and
        % after collision
        if tcInd < Nsim
            plot(tplot(tcInd), yplot(subplotNum,tcInd), 'xk','MarkerSize', 12);
            plot(tplot(tcInd), Xref(subplotNum,tcInd),  'xk','MarkerSize', 12);
            plot(tplot(tcInd+1:end), yplot(subplotNum,tcInd+1:end), 'LineStyle', '-.', 'Color', lineColorArray{i});
            plot(tplot(tcInd+1:end), Xref(subplotNum,tcInd+1:end-1),'LineStyle', ':',  'Color', lineColorArray{i});
        end

        hold off

        xlabel('Time (s)', 'FontSize', 12);
        ylabel(ylabelArray{subplotNum}, 'FontSize', 12);
        title ( titleArray{subplotNum}, 'FontSize', 14);

    end

    legendLog = [legendLog, ['ACC vehicle - ', simOutputArray(i).displayname], ['Target vehicle - ', simOutputArray(i).displayname]];

end

% Adding legend entries
for subPlotNum = 1:2
    legend(h(subPlotNum,:), legendLog);
end

end


function test_suite = test_index_unique
% tests for cosmo_index_unique
%
% #   For CoSMoMVPA's copyright information and license terms,   #
% #   see the COPYING file distributed with CoSMoMVPA.           #
    try % assignment of 'localfunctions' is necessary in Matlab >= 2016
        test_functions=localfunctions();
    catch % no problem; early Matlab versions can use initTestSuite fine
    end
    initTestSuite;

function test_index_unique_()
    n=100;
    a=round(rand(n,1)*2)+1;
    b=round(rand(n,1)*2)+1;
    c=round(rand(n,1)*2)+1;

    abc=[a b c];
    [unq1,unused,idx1]=unique(abc,'rows');
    [idx2,vals2]=cosmo_index_unique({a,b,c});

    assertEqual(unq1,[vals2{:}]);

    nunq=size(unq1,1);
    assert(nunq==numel(idx2));

    % test some random elements
    rp=randperm(nunq);
    rp=rp(1:10);
    for j=rp
        assertEqual(find(idx1==j),idx2{j})
    end

    % test with some string labels
    labels={'a','bb','ccc'}';

    [idx3,vals3]=cosmo_index_unique({labels(a),b,labels(c)});
    assertEqual(idx3,idx2);
    v2to3={labels(vals2{1}),vals2{2},labels(vals2{3})};
    assertEqual(vals3,v2to3)

    % test with matrix input
    [idx4,vals4]=cosmo_index_unique(abc);
    assertEqual(idx4,idx2);
    assertEqual(vals4,[vals2{:}]);

    % test vector input
    rp=randperm(10);
    [idx5,vals5]=cosmo_index_unique(rp+20);
    assertEqual(idx5,{1});
    assertEqual(vals5,rp+20);

    [idx6,vals6]=cosmo_index_unique(rp'+20);
    assertEqual(cell2mat(idx6(rp))',1:numel(rp));
    assertEqual(vals6,(1:numel(rp))'+20);

    % test empty input

    % test wrong inputs
    n=100;
    a=round(rand(n,1)*2)+1;
    b=round(rand(n,1)*2)+1;
    c=round(rand(n,1)*2)+1;

    abc=[a b c];
    [unq1,unused,idx1]=unique(abc,'rows');
    [idx2,vals2]=cosmo_index_unique({a,b,c});

    assertEqual(unq1,[vals2{:}]);

    nunq=size(unq1,1);
    assert(nunq==numel(idx2));

    % test some random elements
    rp=randperm(nunq);
    rp=rp(1:10);
    for j=rp
        assertEqual(find(idx1==j),idx2{j})
    end

    % test with some string labels
    labels={'a','bb','ccc'}';

    [idx3,vals3]=cosmo_index_unique({labels(a),b,labels(c)});
    assertEqual(idx3,idx2);
    v2to3={labels(vals2{1}),vals2{2},labels(vals2{3})};
    assertEqual(vals3,v2to3)

    % test with matrix input
    [idx4,vals4]=cosmo_index_unique(abc);
    assertEqual(idx4,idx2);
    assertEqual(vals4,[vals2{:}]);

    % test vector input
    rp=randperm(10);
    [idx5,vals5]=cosmo_index_unique(rp+20);
    assertEqual(idx5,{1});
    assertEqual(vals5,rp+20);

    [idx6,vals6]=cosmo_index_unique(rp'+20);
    assertEqual(cell2mat(idx6(rp))',1:numel(rp));
    assertEqual(vals6,(1:numel(rp))'+20);

    % test empty input

    % test wrong inputs
    aet=@(x)assertExceptionThrown(@()cosmo_index_unique(x),'');
    aetp=@(x,m)assertExceptionThrown(@()cosmo_index_unique(x),m);
    aet({[1,2],[3]});
    aet({[1,2],'a'});

    if cosmo_wtf('is_matlab')
        v=cosmo_wtf('version');
        is_prior_to_2012b=str2num(v(1))<=7;

        if is_prior_to_2012b
            id_different_classes='MATLAB:CELL:UNIQUE:InputClass';
        else
            id_different_classes='MATLAB:UNIQUE:InputClass';
        end
    else
        id_different_classes='';
    end
    aetp({[1,2],{1,2}},id_different_classes);
    aet(ones([2 2 2]));

function test_unique_with_nans
    n_rows_half=100+ceil(rand()*20);
    %n_rows_half=2;
    n_rows=n_rows_half*2;
    x=ceil(rand(n_rows,2)*sqrt(n_rows_half));

    rp=get_non_identity_randperm(n_rows);

    rp1=rp(1:n_rows_half);
    rp2=rp(n_rows_half+(1:n_rows_half));

    % first column, half of the rows become NaN
    x(rp1,1)=NaN;
    x(rp1,2)=1:n_rows_half;
    x(rp2,1)=1:n_rows_half;
    x(rp2,2)=1:n_rows_half;

    rp_y=get_non_identity_randperm(n_rows);

    y=x(rp_y,:);

    % test with numeric input
    [x_idx,x_unq]=cosmo_index_unique(x);
    [y_idx,y_unq]=cosmo_index_unique(y);

    assertFalse(isequal(x_unq,y_unq));
    % due to NaNs, the indices must be different
    assertFalse(isequal([x_idx{:}],rp_y([y_idx{:}])));

    % test with cell input
    [xx_idx,xx_unq]=cosmo_index_unique({x(:,1),x(:,2)});
    [yy_idx,yy_unq]=cosmo_index_unique({y(:,1),y(:,2)});

    assertFalse(isequal(xx_unq,yy_unq));
    % due to NaNs, the indices must be different
    assertFalse(isequal([xx_idx{:}],rp_y([yy_idx{:}])));

function test_index_unique_empty()
    empty_args={{},{[],{}},{[]},{{},{}}};
    for k=1:numel(empty_args)
        empty_arg=empty_args{k};

        [idxs,unq]=cosmo_index_unique(empty_arg);
        assertTrue(iscell(idxs));
        assertTrue(numel(idxs)==1);
        assertEqual(numel(unq),numel(empty_arg));
        assertTrue(all(cellfun(@isempty,unq)));
    end

function rp=get_non_identity_randperm(n)
    while true
        rp=randperm(n);
        if ~isequal(rp,1:n)
            break;
        end
    end


function g = cell2mat(f)
%CELL2MAT   Convert an array of CHEBTECH objects into an array-valued CHEBTECH.
%   G = CELL2MAT(F) converts the CHEBTECH array F into a single array-valued
%   CHEBTECH G. F should be a vector array (i.e., not a matrix).
%
% Example:
%   f = chebtech.constructor(@sin);
%   g = chebtech.constructor(@cos);
%   h = cell2mat([f g]);
%
% See also MAT2CELL.
%
% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

% TODO: This function is probably not needed anymore.

% Return an empty result:
if ( isempty(f) || numel(f) == 1 )
    g = f;
    return
end

% Extract the coeffs from each of the CHEBTECH objects:
coeffs = { f.coeffs };

% These should all have the same length...
lengths = cellfun(@(x) size(x, 1), coeffs);
minlength = min(lengths);
maxlength = max(lengths);

% If not, then prolong and get the new values:
if ( minlength ~= maxlength )
    for k = 1:numel(f)
        f(k) = prolong(f(k), maxlength);
    end
    coeffs = { f.coeffs };
end

% Append new data to an empty CHEBTECH:
g = f.make(); % Make an empty CHEBTECH.
g.ishappy = min([f.ishappy]);
g.coeffs = cell2mat(coeffs);

end


function draw_shape(x, y, color)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Draw face given x, y
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% inverse y for better visual

bbox = getbbox([x y]);

radius = bbox(4) / 20;

if 1
    %hold on;

    n = size(x,1);

    for i = 1 : n

        r = x(i) - radius/2;
        c = y(i) - radius/2;

        rectangle('Position',[r,c,radius,radius],'Curvature',[1,1],...
            'FaceColor',color);

    end

    %plot(x, y, color);
    %plot(x, y, [color '.']);

    %hold off;
end


%% Copyright (C) 2014, 2016, 2018-2019 Colin B. Macdonald
%% Copyright (C) 2016 Lagu
%%
%% This file is part of OctSymPy.
%%
%% OctSymPy 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.
%%
%% This software 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 software; see the file COPYING.
%% If not, see <http://www.gnu.org/licenses/>.

%% -*- texinfo -*-
%% @documentencoding UTF-8
%% @defmethod @@sym imag (@var{z})
%% Imaginary part of a symbolic expression.
%%
%% Examples:
%% @example
%% @group
%% syms z
%% imag(z)
%%   @result{} ans = (sym) im(z)
%% @end group
%%
%% @group
%% syms x real
%% imag(x)
%%   @result{} ans = (sym) 0
%% imag(1i*x)
%%   @result{} ans = (sym) x
%% @end group
%%
%% @group
%% imag([x  sym(pi) + 6i  7  3i])
%%   @result{} ans = (sym) [0  6  0  3]  (1×4 matrix)
%% @end group
%% @end example
%%
%% @seealso{@@sym/real, @@sym/conj, @@sym/ctranspose}
%% @end defmethod

function y = imag(z)
  if (nargin ~= 1)
    print_usage ();
  end

  y = elementwise_op ('im', z);

end

%!assert (isequal (imag (sym (4) + 3i),3))

%!test
%! syms x y real
%! z = x + 1i*y;
%! assert (isequal (imag (z),y))

%!test
%! syms x y real
%! Z = [4  x + 1i*y; 1i*y 4 + 3i];
%! assert (isequal (imag (Z),[0 y; y 3]))

%!test
%! syms x real
%! d = exp (x*i);
%! assert (isequal (imag (d), sin (x)))

%!test
%! % round trip
%! syms x
%! d = 3 - 5i;
%! f = imag (x);
%! A = imag (d);
%! h = function_handle (f);
%! B = h (d);
%! assert (A, B)


function r = isintval(a)
%ISINTVAL     Returns 1 if  a  is intval
%
%   r = isintval(a)
%

% written  09/02/00     S.M. Rump
% modified 04/04/04     S.M. Rump  set round to nearest for safety
% modified 04/06/05     S.M. Rump  rounding unchanged
%

  r = logical(1);


% op_CSImakeK0fid.m
% Jamie Near & Sneha Senthil, Sunnybrook Research Insitute 2021.
%
% USAGE:
% out=op_CSImakeK0fid(in,start,interval);
%
% DESCRIPTION:
% Extract points with repeating periodic interval from a spatio-spectral
% MRSI signal.  This is useful for Rosette MRSI data, when we want to extract
% the FIDs from the centre of k-space.
%
% INPUTS:
% in        = input MRSI data in matlab structure format.
% start     = The index of the first point to extract.
% interval  = The interval between subsequently extracted points.
%
% OUTPUTS:
% out       = Extracted FID.

function out=op_CSImakeK0fid(in,start,interval);

%extract the desired points;
fids=in.data(start:interval:end,:,:,:);

%recalculate the size;
sz=size(fids);

%remake the time vector:
t=in.adcTime(start:interval:end);

%dwelltime
dwelltime=t(2)-t(1);

%FILLING IN DATA STRUCTURE
out=in;
out.fids=fids;
out.sz=sz;
out.t=t;
out.dwelltime=dwelltime;
out.sw=1/dwelltime;

%FILLING IN THE FLAGS
out.flags=in.flags;
out.flags.writtentostruct=1;


%fill_prmm Compute the null-space and fill PRMM.
%
%   Parameters:

function [P,X, u1,u2, lambda, info] = fill_prmm(M, Idepths, central, ...
                                                opt, info)

[NULLSPACE, result] = create_nullspace(M, Idepths, central, ...
                                       opt.create_nullspace);

info.create_nullspace             = opt.create_nullspace;
info.sequence{end}.tried          = result.tried;
info.sequence{end}.tried_perc     = result.tried/comb(size(M,2), 4) *100;
info.sequence{end}.used           = result.used;
info.sequence{end}.used_perc      = result.used/result.tried *100;
info.sequence{end}.failed         = result.failed;
info.sequence{end}.size_nullspace = size(NULLSPACE);

if opt.verbose
  disp(sprintf('Tried/used: %d/%d (%.1e %%/ %2.1f %%)', result.tried, ...
               result.used, info.sequence{end}.tried_perc, ...
               info.sequence{end}.used_perc));
  disp(sprintf('%d x %d'' is size of the nullspace', size(NULLSPACE,2), ...
               size(NULLSPACE,1))); end

[m,n] = size(M); m = m/3;

if size(NULLSPACE,2) == 0
  P = []; X = []; u1 = 1:m; u2 = 1:n; lambda=[];
else
  r = 4;
  [L, S] = nullspace2L(NULLSPACE, r, opt);
  clear NULLSPACE;

  if isempty(opt.create_nullspace), threshold = .01;
  else, threshold = opt.create_nullspace.threshold; end
  if svd_suff_data(S, r, threshold)
    if opt.verbose
      dS = diag(S); %disp(diag(S));
      fprintf(1,'Smallest 2r singular values:%s.\n', sprintf(' %f', ...
                                                  dS(end-2*r:end))); end
    [Mdepths, lambda] = L2depths(L, M, Idepths, opt); info.Mdepths = Mdepths;
    [P,X, u1b,u2] = approximate(Mdepths, r, L, opt);
    u1 = union(ceil(u1b/3),[]); killb = setdiff(k2i(u1),u1b);
    if ~isempty(killb), r1b = setdiff(1:3*m,u1b); kill = killb;
      for ib = killb(1:end-1), lower = find(killb > ib);
        if kill(lower(1)-1) < kill(lower(1)) - 1,
          kill(lower) = kill(lower) -1; end; end
      P = P(setdiff(1:length(r1b),kill),:);end
    lambda = lambda(setdiff(1:m,u1),setdiff(1:n,u2)); % to fit P*X
  else P=[]; X=[]; u1=1:m; u2=1:n; lambda=[]; end
end

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

function [L,S] = nullspace2L(NULLSPACE, r, opt)
% Compute the basis of MM (L) from the null-space.

if opt.verbose, fprintf(1,'Computing the basis...'); tic; end
if size(NULLSPACE,2) < 10*size(NULLSPACE,1) % orig:[A,S,U] = svd(NULLSPACE',0);
  [U,S,V]             = svd(NULLSPACE);
else
  [U,SS]              = eig(NULLSPACE*NULLSPACE');
  dSS                 = diag(SS); l = length(dSS);
  [sortdSS(l:-1:1),I] = sort(dSS); I(l:-1:1) = I;  U = U(:,I);
  sortdSS             = max(sortdSS,zeros(1,l)); sortdSS=sqrt(sortdSS);
  for i=1:l, S(i,i) = sortdSS(i); end
end

lenU = length(U);
L = U(:,lenU+1-r:lenU);
if opt.verbose, disp(['(' num2str(toc) ' sec)']); end

function r = comb(n,k)

% returns combination number n over k

r=1;
for i=1:k
  r=r*(n-i+1)/i;
end

function y = svd_suff_data(S,r, threshold)

% S is the singular value part of the svd of the nullspaces of the column
% r-tuples.  We'll want to be able to take the r least significant columns
% of U.  This is right because the columns of U should span the whole space
% that M's columns might span.  That is, M is FxP.  The columns of U should
% span the F-dimensional Euclidean space, since U is FxF.  However, we want
% to make sure that the F-r-1'th singular value of S isn't tiny.  If it is,
% our answer is totally unreliable, because the nullspaces of the column
% r-tuples don't have sufficient rank.  If this happens, it means that the
% intersection of the column cross-product spaces is bigger than r-dimensional,
% and randomly choosing an r-dimensional subspace of that isn't likely to
% give the right answer.

Snumcols = size(S,2);
Snumrows = size(S,1);
if (Snumrows == 0 | Snumcols + r < Snumrows | Snumrows <= r)
  y = 0;
else
  y = S(Snumrows-r,Snumrows-r) > threshold;
end


function [nodes, edges, faces] = gcontour3d(img)
%GCONTOUR3D Create contour graph of a 3D binary image.
%
%
%
%   ---------
%
%   author : David Legland
%   INRA - TPV URPOI - BIA IMASTE
%   created the 28/06/2004.
%

nodes = zeros([0 3]);   % 3 coordinates vertices
edges = zeros([0 2]);   % first node and second nodes
faces = zeros([0 4]);   % indices of 4 corners of each square face

D1 = size(img, 1);
D2 = size(img, 2);
D3 = size(img, 3);

% first direction for image
for y = 1:D2
    for z = 1:D3
        % find transitions between the two phases
        ind = find(img(1:D1-1, y, z)~=img(2:D1, y, z));

        % process each transition in direction 1
        for i2 = 1:length(ind)

            % coordinates of each node
            n1 = [ind(i2)+.5 y-.5 z-.5];
            n2 = [ind(i2)+.5 y-.5 z+.5];
            n3 = [ind(i2)+.5 y+.5 z+.5];
            n4 = [ind(i2)+.5 y+.5 z-.5];

            % add the face (and edges) with the 4 given nodes
            [nodes, edges, faces] = addFace(nodes, edges, faces, [n1; n2; n3; n4]);
        end
    end
end

% second direction for image
for x=1:D1
    for z=1:D3
        % find transitions between the two phases
        ind = find(img(x, 1:D2-1, z)~=img(x, 2:D2, z));

        % process each transition in direction 1
        for i2 = 1:length(ind)

            % coordinates of each node
            n1 = [x-.5 ind(i2)+.5 z-.5];
            n2 = [x-.5 ind(i2)+.5 z+.5];
            n3 = [x+.5 ind(i2)+.5 z+.5];
            n4 = [x+.5 ind(i2)+.5 z-.5];

            % add the face (and edges) with the 4 given nodes
            [nodes, edges, faces] = addFace(nodes, edges, faces, [n1; n2; n3; n4]);
        end
    end
end

% third direction for image
for x=1:D1
    for y=1:D2
        % find transitions between the two phases
        ind = find(img(x, y, 1:D3-1)~=img(x, y, 2:D3));

        % process each transition in direction 1
        for i2 = 1:length(ind)

            % coordinates of each node
            n1 = [x-.5 y-.5 ind(i2)+.5];
            n2 = [x-.5 y+.5 ind(i2)+.5];
            n3 = [x+.5 y+.5 ind(i2)+.5];
            n4 = [x+.5 y-.5 ind(i2)+.5];

            % add the face (and edges) with the 4 given nodes
            [nodes, edges, faces] = addFace(nodes, edges, faces, [n1; n2; n3; n4]);
        end
    end
end

return;

function [nodes, edges, faces] = addFace(nodes, edges, faces, faceNodes)
% add given nodes and coresponding face to the graph.

n1 = faceNodes(1,:);
n2 = faceNodes(2,:);
n3 = faceNodes(3,:);
n4 = faceNodes(4,:);

% search indices of each nodes
ind1 = find(ismember(nodes, n1, 'rows'));
ind2 = find(ismember(nodes, n2, 'rows'));
ind3 = find(ismember(nodes, n3, 'rows'));
ind4 = find(ismember(nodes, n4, 'rows'));

% if nodes are not in the list, we add them
if isempty(ind1)
    nodes = [nodes; n1];
    ind1 = size(nodes, 1);
end
if isempty(ind2)
    nodes = [nodes; n2];
    ind2 = size(nodes, 1);
end
if isempty(ind3)
    nodes = [nodes; n3];
    ind3 = size(nodes, 1);
end
if isempty(ind4)
    nodes = [nodes; n4];
    ind4 = size(nodes, 1);
end

% add current face to the list
faces(size(faces, 1)+1, 1:4) = [ind1(1) ind2(1) ind3(1) ind4(1)];

% create edges of the face
% (first index is the smallest one, by convention)
e1 = [min(ind1, ind2) max(ind1, ind2)];
e2 = [min(ind2, ind3) max(ind2, ind3)];
e3 = [min(ind3, ind4) max(ind3, ind4)];
e4 = [min(ind4, ind1) max(ind4, ind1)];

 % if nodes are not in the list, we add them
if isempty(find(ismember(edges, e1, 'rows'), 1))
    edges = [edges; e1];
end
if isempty(find(ismember(edges, e2, 'rows'), 1))
    edges = [edges; e2];
end
if isempty(find(ismember(edges, e3, 'rows'), 1))
    edges = [edges; e3];
end
if isempty(find(ismember(edges, e4, 'rows'), 1))
    edges = [edges; e4];
end


function [y, x] = max(f, flag, dim)
%MAX   Maximum value of a CHEBFUN.
%   MAX(F) and MAX(F, 'global') return the maximum value of the CHEBFUN F.
%
%   [Y, X] = MAX(F) returns also a point X such that F(X) = Y.
%
%   [Y, X] = MAX(F, 'local') returns not just the global maximum value but all
%   of the local maxima.
%
%   If F is complex-valued, absolute values are taken to determine the maxima,
%   but the resulting values correspond to those of the original function.
%
%   If F is array-valued, then the columns of X and Y correspond to the columns
%   of F. NaNs are used to pad Y and X when the 'local' flag is used and the
%   columns are not of the same length.
%
%   H = MAX(F, G), where F and G are CHEBFUNs defined on the same domain,
%   returns a CHEBFUN H such that H(x) = max(F(x), G(x)) for all x in the
%   domain of F and G. Alternatively, either F or G may be a scalar.
%
%   MAX(F, [], DIM) computes the maximum of the CHEBFUN F in the dimension DIM.
%   If DIM = 1 and F is a column CHEBFUN or DIM = 2 and F is a row CHEBFUN, this
%   is equivalent to MAX(F). Otherwise, MAX(F, [], DIM) returns a CHEBFUN which
%   is the maximum across the discrete dimension of F. For example, if F is a
%   quasimatrix with two columns, MAX(F, [], 2) = MAX(F(:,1), F(:,2)).
%
% See also MIN, MINANDMAX, ROOTS.

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

% Trivial empty case.
if ( isempty(f) )
    x = [];
    y = [];
    return
end

if ( nargin == 3 )
    if ( ~any(dim == [1 2]) )
        error('CHEBFUN:CHEBFUN:max:badDim', ...
            'DIM input to CHEBFUN MAX must be 1 or 2.');
    end

    if ( dim ~= 1 + f(1).isTransposed )
        % Take max across discrete dimension of a quasimatrix:
        f = cheb2cell(f);
        y = -realmax;
        for k = 1:numel(f)
            y = maxOfTwoChebfuns(y, f{k});
        end
        y = merge(y);
        return
    end
end

if ( (nargin > 2) && isempty(flag) )
    % MAX(F, [], 1).
    flag = 'global';
end

if ( (nargin == 1) || strcmp(flag, 'global') )
    % MAX(F) or MAX(F, 'global')
    [y, x] = globalMax(f);

elseif ( isa(flag, 'chebfun') || isnumeric(flag) )
    % MAX(F, G)
    y = maxOfTwoChebfuns(f, flag);

elseif ( strcmp(flag, 'local') )
    % MAX(F, 'local')
    [y, x] = localMax(f);

else
    error('CHEBFUN:CHEBFUN:max:flag', 'Unrecognized flag.');

end

end

%% %%%%%%%%%%%%%%%%%%%%%%%%%%%% GLOBAL MAXIMUM %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [y, x] = globalMax(f)

% Call MINANDMAX():
[y, x] = minandmax(f);

% Extract the maximum:
y = y(2,:);
x = x(2,:);

end

%% %%%%%%%%%%%%%%%%%%%%%%%%%%%% LOCAL MAXIMA %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [y, x] = localMax(f)

% Call MINANDMAX():
[y, x] = minandmax(f, 'local');

% Determine which are maxima.

ends = f(1).domain([1, end]).'; % Endpoints of the domain are special.
f = mat2cell(f); % Convert f into a cell of scalar-valued CHEBFUNs.

% Loop over the FUNs:
for k = 1:numel(f)
    % Compute 1st and 2nd derivatives.
    dfk = diff(f{k});
    dfk2 = diff(dfk);

    % For interior extrema, look at 2nd derivative:
    maximaLoc = feval(diff(f{k}, 2), x(:,k)) < 0;

    % For end-points, look at 1st derivative:
    dfk_ends = feval(dfk, ends);
    endptMaxLoc = dfk_ends.*[1, -1]' < 0;

    % If 1st derivative is small at an endpoint, assume it's zero and try to
    % use 2nd derivative to determine if it's a minimum:
    %
    % [TODO]:  What if the 2nd derivative is zero, so that rounding error
    % precludes us from accurately determining the sign?
    smallEndDer = abs(dfk_ends) < 1e3*vscale(dfk)*eps;
    endptMaxLoc(smallEndDer) = feval(dfk2, ends(smallEndDer)) < 0;

    maximaLoc(1) = endptMaxLoc(1);
    maximaLoc(x(:,k) == ends(2)) = endptMaxLoc(2);

    % Set points corresponding to local maxima to NaN:
    y(~maximaLoc,k) = NaN;
    x(~maximaLoc,k) = NaN;

    % Sort the result
    [x(:,k), maximaLoc] = sort(x(:,k));
    y(:,k) = y(maximaLoc,k);
end

% Remove any rows which contain only NaNs.
x(all(isnan(x), 2),:) = [];
y(all(isnan(y), 2),:) = [];

end

%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%% MAX(F, G) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function h = maxOfTwoChebfuns(f, g)
% Return the function h(x) = max(f(x), g(x)) for all x.

% If one is complex, use abs(f) and abs(g) to determine which function values to
% keep. (experimental feature)
if ( isreal(f) && isreal(g) && (nargin < 3) )
	S = sign(f - g);
else
	S = sign(abs(f) - abs(g));
end

% Heaviside function (0 where f > g, 1 where f < g);
H = 0.5*(S + 1);
notH = 0.5*(1 - S); % ~H.

% Combine for output:
h = H.*f + notH.*g;

% [TODO]: Enforce continuity?

% TODO: Why simplify?
% Simplify:
h = simplify(h);

end


function [nlp] = updateDesiredGait(nlp, sys, target_gait)
    domains = sys.Gamma.Nodes.Domain;

    idx = [7:9, 12:14];
    for j=1:numel(domains) %% HECK!!!!!!
        domain = domains{j};
        phase_idx = getPhaseIndex(nlp,sys.Gamma.Nodes.Name{j});
        for i = 1:nlp.Phase(phase_idx).NumNode
            x_star_val = target_gait(j).states.x(idx, i);
            dx_star_val = target_gait(j).states.dx(idx, i);
%             u_star_val = target_gait(j).inputs.u(:,i);
            updateCostProp(nlp.Phase(phase_idx), ['stateDeviation_', domain.Name], i, 'AuxData',...
                {{x_star_val, dx_star_val}});
        end
    end
    nlp.update();
end


function B = multitransp(A, unused) %#ok<INUSD>
% Transpose the matrix slices of an N-D array (no complex conjugate)
%
% function B = multitransp(A)
%
% If A is a 3-D array, then B is a 3-D array such that
%
%     B(:, :, i) = A(:, :, i).'
%
% for each i. If A is an N-D array, then B is an N-D array with the slices
% A(:, :, i, j, k, ...) transposed.
%
% This function is just a wrapper for pagetranspose, with a fallback call
% to multitransp_legacy in case pagetranspose is not available.
% If pagetranspose is available, it is better to call it directly.
% Note that pagemtimes also allows to compute products with transposes
% without explicitly transposing arrays.
%
% See also: multiprod multihconj multiscale multiskew multiskewh multitrace

% This file is part of Manopt: www.manopt.org.
% Original author: Nicolas Boumal, Aug. 12, 2021.
% Contributors: Xiaowen Jiang
% Change log:
%
%   Aug. 12, 2021 (NB):
%       Matlab R2020b introduced a built-in function pagetranspose which
%       does essentially everything we ever needed to do with multitransp
%       in Manopt. Accordingly, multitransp became a wrapper for
%       pagetranspose, and the old code for multitransp remains available
%       as multitransp_legacy.

    assert(nargin == 1, ...
           'The new multitransp only takes one input. Check multitransp_legacy.');

    if exist('pagetranspose', 'file') % Added to Matlab R2020b
        B = pagetranspose(A);
    else
    %   warning('manopt:multi', ...
    %          ['Matlab R2020b introduced pagetranspose.\n' ...
    %           'Calling the old code multitransp_legacy instead.\n' ...
    %           'To disable this warning: warning(''off'', ''manopt:multi'')']);
        B = multitransp_legacy(A);
    end

end


function E =extA(A, c)
%------------------------------------------------------------------------------
%
% Design and implementation by:
% Dr. Paul M. de Zeeuw <Paul.de.Zeeuw@cwi.nl>  http://homepages.cwi.nl/~pauldz/
% Last Revision: June 6, 1999.
% (c) 1999-2002 Stichting CWI, Amsterdam
%------------------------------------------------------------------------------
E=extU(extD(extR(extL(A, c), c), c), c);
%------------------------------------------------------------------------------


%% Testing SCIP Option Setting Methods
% 17/5/15
clc
clear
% Default Problem
a = 100;
fun = @(x) a*(x(2) - x(1)^2)^2 + (1 - x(1))^2;
lb = [-1.5; -1.5]; ub = [2;2]; x0 = [-2; 1];
%Convert to Ins List
zs = zeros(size(x0));
x = scipvar(size(x0));
fs = fun(x); nl.obj_instr = fs.ins; nl.obj_val = fun(x0); nl.x0 = x0;
opts = optiset('solver','scip');

%% No scipopts Field
sopts = [];
sopts.maxiter = 1;
sopts.display = 3;
scip([],zs,[],[],[],lb,ub,[],[],[],nl,sopts)

%% Not a cell array
sopts.scipopts = 'a';
scip([],zs,[],[],[],lb,ub,[],[],[],nl,sopts)

%% Cell array of one column
sopts.scipopts = {'a';1};
scip([],zs,[],[],[],lb,ub,[],[],[],nl,sopts)

%% Empty Name
sopts.scipopts = {'',1};
scip([],zs,[],[],[],lb,ub,[],[],[],nl,sopts)

%% Empty Val (will skip it)
sopts.scipopts = {'a',[]};
scip([],zs,[],[],[],lb,ub,[],[],[],nl,sopts)

%% Unknown Option
sopts.scipopts = {'limits/times',1};
scip([],zs,[],[],[],lb,ub,[],[],[],nl,sopts)

%% Wrong Datatype for Option
sopts.scipopts = {'limits/time','a'};
scip([],zs,[],[],[],lb,ub,[],[],[],nl,sopts)

%% Outside Option Limits
sopts.scipopts = {'limits/time',-1};
scip([],zs,[],[],[],lb,ub,[],[],[],nl,sopts)

%% Bool Option logical
sopts.scipopts = {'display/lpinfo',false};
scip([],zs,[],[],[],lb,ub,[],[],[],nl,sopts)

%% Bool Option double
sopts.scipopts = {'display/lpinfo',1};
scip([],zs,[],[],[],lb,ub,[],[],[],nl,sopts)

%% Char Option not char
clc
sopts.scipopts = {'lp/initalgorithm',1};
scip([],zs,[],[],[],lb,ub,[],[],[],nl,sopts)

%% Char Option not valid char
clc
sopts.scipopts = {'lp/initalgorithm','a'};
scip([],zs,[],[],[],lb,ub,[],[],[],nl,sopts)

%% Char Option
clc
sopts.scipopts = {'lp/initalgorithm','p'};
scip([],zs,[],[],[],lb,ub,[],[],[],nl,sopts)

%% String Option not string
clc
sopts.scipopts = {'nlp/solver',1};
scip([],zs,[],[],[],lb,ub,[],[],[],nl,sopts)

%% String Option unknown
clc
sopts.scipopts = {'nlp/solver','a'};
scip([],zs,[],[],[],lb,ub,[],[],[],nl,sopts)

%% opti_scipnl test
clc
fun = @(x) -x(1)*x(2)*x(3);
nlcon = @(x) -x(1)^2 - 2*x(2)^2 - 4*x(3)^2 + 48;
cl = 0;
cu = inf;
x0 = [1;1;1];
opts = optiset('display','iter','solverOpts',scipset('scipopts',{'nlp/disable',true}));
opti_scipnl(fun,[],[],[],[],[],nlcon,cl,cu,[],x0,opts);

%% opti test
clc
fun = @(x) (x(1)-x(2))^2 + (x(3)-1)^2 + (x(4)-1)^4 + (x(5)-1)^6;
nlcon = @(x) [x(1) + x(2) + x(3) + 4*x(4) - 7;
              x(3) + 5*x(5) - 6];
cl = [0;0];
cu = [0;0];
x0 = [10;7;2;-3;0.8];

sopts = scipset('scipopts',{'limits/solutions',1}); %find first feasible solution
opts = optiset('solver','scip','display','iter','solverOpts',sopts);
Opt = opti('fun',fun,'nl',nlcon,cl,cu,'x0',x0,'opts',opts)

[x,f,e,i] = solve(Opt)

%% MIQCQP1 [-2.5429] [non-convex]
clc
% Objective
H = eye(2); f = [-2;-2];
% Linear Constraints
A = [-1 1; 1 3]; b = [2;5];
% Quadratic Constraint
Q = [1 0;0 1]; l = [0;-2];
qrl = 3.5; qru = 5;
% Bounds
lb = [0;0]; ub = [40;inf];
% Integrality
xtype = 'IC';
% Options
sopts = scipset('scipopts',{'propagating/obbt/freq',1;...
                            'propagating/obbt/itlimitfactor',0;
                            'constraints/quadratic/maxproprounds',10;
                            'separating/maxrounds',20;
                            'limits/solutions',1});
opts = optiset('solver','scip','display','iter','solverOpts',sopts);
%Build & Solve
Opt = opti('H',H,'f',f,'ineq',A,b,'qcrow',Q,l,qrl,qru,'bounds',lb,ub,'xtype',xtype,'opts',opts)
[x,fval,exitflag,info] = solve(Opt)
% Plot
plot(Opt)

%% Global Emphasis
clc
fun = @(x) -x(1)*x(2)*x(3);
nlcon = @(x) -x(1)^2 - 2*x(2)^2 - 4*x(3)^2 + 48;
cl = 0;
cu = inf;
x0 = [1;1;1];

sopts = scipset('globalEmphasis','hardlp');
opts = optiset('solver','scip','display','iter','solverOpts',sopts);
Opt = opti('fun',fun,'nl',nlcon,cl,cu,'x0',x0,'opts',opts)

[x,f,e,i] = solve(Opt)

%% Presolving Emphasis
clc
fun = @(x) -x(1)*x(2)*x(3);
nlcon = @(x) -x(1)^2 - 2*x(2)^2 - 4*x(3)^2 + 48;
cl = 0;
cu = inf;
x0 = [1;1;1];

sopts = scipset('presolvingEmphasis','fast');
opts = optiset('solver','scip','display','iter','solverOpts',sopts);
Opt = opti('fun',fun,'nl',nlcon,cl,cu,'x0',x0,'opts',opts)

[x,f,e,i] = solve(Opt)

%% Heuristics Emphasis
clc
fun = @(x) -x(1)*x(2)*x(3);
nlcon = @(x) -x(1)^2 - 2*x(2)^2 - 4*x(3)^2 + 48;
cl = 0;
cu = inf;
x0 = [1;1;1];

sopts = scipset('heuristicsEmphasis','aggressive');
opts = optiset('solver','scip','display','iter','solverOpts',sopts);
Opt = opti('fun',fun,'nl',nlcon,cl,cu,'x0',x0,'opts',opts)

[x,f,e,i] = solve(Opt)

%% Separating Emphasis
clc
fun = @(x) -x(1)*x(2)*x(3);
nlcon = @(x) -x(1)^2 - 2*x(2)^2 - 4*x(3)^2 + 48;
cl = 0;
cu = inf;
x0 = [1;1;1];

sopts = scipset('separatingEmphasis','aggressive');
opts = optiset('solver','scip','display','iter','solverOpts',sopts);
Opt = opti('fun',fun,'nl',nlcon,cl,cu,'x0',x0,'opts',opts)

[x,f,e,i] = solve(Opt)


function gescatter(filename,lon,lat,c,varargin)
% GESCATTER - create a scatter plot in Google Earth
%
%   GESCATTER(FILENAME,LON,LAT,C) - creates a .kml file that
%   displays colored circles at the locations specified by the
%   vectors LON and LAT similar to ML's builtin function, SCATTER.
%   The color of the circles is scaled relative to the
%   values provided in third input, C.
%
%   OPTIONS AND SYNTAX - Optional inputs are entered as
%   property/value pairs.  Valid optional properties are:
%
%   GESCATTER(...,'colormap','hot') - uses Matlabs 'hot'
%   colormap instead of the default (jet). Also accepts function
%   handles (@hot), or custom colormaps (m x 3 matrices).
%
%   GESCATTER(...,'clims',[low high]) - limit the color
%   values to a specified values (similar to CAXIS on a ML
%   figure). Clims should be supplied as a 2-element array.
%
%   GESCATTER(...,'time',timevector) - assigns a time to each
%   point. The length of the timevector array should be the same
%   as LAT, LON, and C.
%
%   GESCATTER(...,'scale',size) - scales the size of the dots in the
%   Google Earth file.  Default value is 0.4.
%
%   EXAMPLE
%
%   %generate some data
%   x=(0:0.05:6*pi);
%   lon = -122.170087 + cos(x)*0.01;
%   lat = 37.455697 + x*0.001;
%
%   %color the points according to their latitude
%   gescatter('foo.kml',lon,lat,lat)
%
% SEE ALSO scatter

% A. Stevens @ USGS 3/04/2009
% astevens@usgs.gov

%default values
clims=[min(c) max(c)];
cmap=fliplr(jet);
t=[];
scale=0.5;

%parse inputs and do some error-checking
if nargin>0
    [m,n]=size(varargin);
    opts={'clims','time','scale','colormap'};
    for i=1:n;
        indi=strcmpi(varargin{i},opts);
        ind=find(indi==1);
        if isempty(ind)~=1
            switch ind
                case 1
                    clims=varargin{i+1};
                    if numel(clims)~=2
                        error('Clims should be a two-element array.')
                    end

                case 2
                    t=varargin{i+1};
                    if any(isnan(t))
                        error('Time vector should not contain NaNs.')
                    end
                    if ~isnumeric(t)
                        error('Time should be entered in ML Datenum format.')
                    end
                case 3
                    scale=varargin{i+1};
                case 4
                    cmap=varargin{i+1};
                    %check size of numeric colormap input
                    if isa(cmap,'numeric')
                        [m,n]=size(cmap);
                        if n~=3
                            error('Custom colormap must have 3 columns.')
                        end
                        cmap=fliplr(cmap);
                    else
                        %if standard colormap is supplied
                        if isa(cmap,'function_handle')
                            cmap= func2str(cmap);
                        end
                        cmap=fliplr(feval(cmap));
                    end

            end
        end
    end
end

[pathstr,namer] = fileparts(filename);

%get rid on nans
gind=(isfinite(lon) & isfinite(lat) & isfinite(c));
lon=lon(gind);
lat=lat(gind);
c=c(gind);

%figure out the rgb colors of each value
cvals=[-inf;linspace(clims(1),clims(2),...
    length(cmap(:,1))-2)';inf];
[n,bin]=histc(c,cvals);
colors=cmap(bin,:);

%convert to GE's hex format
rgb=cellfun(@(x)(dec2hex(floor(x.*255),2)),...
    num2cell(colors),'uni',0);

%write the GE file
header=['<?xml version="1.0" encoding="UTF-8"?>',...
    '<kml xmlns="http://www.opengis.net/kml/2.2">',...
    '<Document><name>',namer,'</name>'];
footer='</Document></kml>';

h = waitbar(0,'Creating file, Please wait...');
set(h,'name','Creating Google Earth file')

fid = fopen(filename, 'wt');
fprintf(fid, '%s \n',header);

for i=1:length(lon)

    %create a style to hide each point in one document
    fprintf(fid,'%s \n','');

    %define the point style
    fprintf(fid,'%s \n','');

    %add the placemark
    fprintf(fid, '%s \n','<Placemark>');
    fprintf(fid,'%s \n','');

    %create a simple description for each point
    fprintf(fid, '%s \n','<description><![CDATA[

');
    fprintf(fid, '%s \n',['
Filename: ',namer,'
']);
    fprintf(fid, '%s \n',['
Value: ',sprintf('%.1f',c(i)),'
']);
    if ~isempty(t)
        fprintf(fid, '%s \n',['Time (GMT): ',datestr(t(i)),'
']);
    end
    fprintf(fid, '%s \n',']]></description>');

    fprintf(fid,'%s \n','<Point>');
    fprintf(fid,'%s','<coordinates>');
    fprintf(fid, ' %.6f, %.6f, %.2f', [lon(i) lat(i) c(i)]);
    fprintf(fid,'%s \n','</coordinates>');
    fprintf(fid,'%s \n','</Point>');

    if ~isempty(t)
        fprintf(fid,'%s \n','<TimeSpan>');
        fprintf(fid,'%s \n',['<begin>',datestr(t(1),29),...
            'T',datestr(t(1),13),'Z</begin>']);
        fprintf(fid,'%s \n',['<end>',datestr(t(end),29),...
            'T',datestr(t(end),13),'Z</end>']);
        fprintf(fid,'%s \n','</TimeSpan>');
    end

    fprintf(fid, '%s \n','</Placemark>');

    waitbar(i/length(lon),h,sprintf('%d%% complete...',...
        round((i/length(lon))*100)));

end

fprintf(fid, '%s \n','');
fprintf(fid, '%s \n',footer);

close(h);
fclose(fid);


function symb_pvec = amplexpr(pvec)
%AMPLEXPR Converts SDPVAR variable to AMPL string

for pi = 1:size(pvec,1)
    for pj = 1:size(pvec,2)
        p = pvec(pi,pj);

        if isa(p,'double')
            symb_p = num2str(p);
        else
            LinearVariables = depends(p);
            x = recover(LinearVariables);
            exponent_p = full(exponents(p,x));
            names = cell(length(LinearVariables),1);
            for i = 1:length(LinearVariables)
                names{i}=['x[' num2str(LinearVariables(i)) ']'];
            end

            symb_p = '';
            if all(exponent_p(1,:)==0)
                symb_p = num2str(getbasematrix(p,0));
                exponent_p = exponent_p(2:end,:);
            end

            for i = 1:size(exponent_p,1)
                coeff = getbasematrixwithoutcheck(p,i);
                switch full(coeff)
                    case 1
                        coeff='+';
                    case -1
                        coeff = '-';
                    otherwise
                        if coeff >0
                            coeff = ['+' num2str2(coeff)];
                        else
                            coeff=num2str2(coeff);
                        end
                end
                if strcmp(symb_p,'') & (strcmp(coeff,'+') | strcmp(coeff,'-'))
                    symb_p = [symb_p coeff symbmonom(names,exponent_p(i,:))];
                else
                    symb_p = [symb_p coeff '*' symbmonom(names,exponent_p(i,:))];
                end
            end
            if symb_p(1)=='+'
                symb_p = symb_p(2:end);
            end
        end

        symb_p = strrep(symb_p,'+*','+');
        symb_p = strrep(symb_p,'-*','-');
        symb_pvec{pi,pj} = symb_p;
    end
end

function s = symbmonom(names,monom)
s = '';
for j = 1:length(monom)
    if monom(j)>0
        if strcmp(s,'')
            s = [s names{j}];
        else
            s = [s '*' names{j}];
        end
    end
    if monom(j)>1
        s = [s '^' num2str(monom(j))];
    end
end

function s = num2str2(x)
s = num2str(x);
if isequal(s,'1')
    s = '';
end
if isequal(s,'-1')
    s = '-';
end


% This function do a cross validation test of DNN.
%
% Author: Xiong Xiao, NTU
% Date Created: 10 Oct 2013
% Last Modified: 20 Jul 2015
%
function [cost,cost_pure,subcost,subacc] = CrossValidationTest_tree4(layer, data, para)
para.NET.sentenceMinibatch = 1;
[sentIdxInBlock] = shuffle_data(layer, para, data, 1);  nBlock = length(sentIdxInBlock);
if isfield(para.NET, 'nSequencePerMinibatchCV')
    para.NET.nSequencePerMinibatch = para.NET.nSequencePerMinibatchCV;
end

cost_cv = [];
if para.useGPU; 	cost_cv= gpuArray(cost_cv);        end
cost_cv_pure = cost_cv; subcost = cost_cv;    subacc = cost_cv;

nTestSample = [];
for blk_i = 1:nBlock
    pause(.1);
    if para.useGPU && para.displayGPUstatus==1
        gpuStatus = gpuDevice;
        fprintf('GPU has %2.2E free memory!\n', gpuStatus.FreeMemory);
    end

    [block_data] = BlockDataGeneration(data, para, sentIdxInBlock{blk_i});
    [minibatch] = MinibatchPackaging_tree4(block_data, para);

    clear cost_func
    for batch_i=1:minibatch.nBatch
        batch_data = GetMinibatch2(minibatch, para, batch_i);
        for si=1:length(batch_data)
            batch_nFr(si) = size(batch_data{si},2);
        end

        % Evaluate the cost function and gradient on current batch
        nTestSample(end+1) = max(batch_nFr);
        [cost_func(batch_i)] = DNN_Cost10(layer, batch_data, para, 2);

        if mod(batch_i,para.displayInterval)==0 || (batch_i==minibatch.nBatch && para.displayInterval>batch_i)
            fprintf('CV cost at utterance %d/%d = %.4g / %.4g - %s\n', ...
                batch_i, minibatch.nBatch, mean([cost_func(max(1,batch_i-para.displayInterval+1) : batch_i).cost]), ...
                mean([cost_func(max(1,batch_i-para.displayInterval+1) : batch_i).cost_pure]), datestr(now));
        end
    end
    cost_cv = [cost_cv [cost_func(:).cost]];
    cost_cv_pure = [cost_cv_pure [cost_func(:).cost_pure]];
    subcost = [subcost [cost_func(:).subcost]];
    subacc = [subacc [cost_func(:).subacc]];
    clear minibatch;
    layer = clean_network_layer(layer);
end

subcost =  gather(subcost * nTestSample') / sum(nTestSample);
cost = gather(cost_cv(:)' * nTestSample') / sum(nTestSample);
cost_pure = gather(cost_cv_pure(:)' * nTestSample') / sum(nTestSample);
if isfield(cost_func(1),'subacc')
    subacc =  gather(subacc * nTestSample') / sum(nTestSample);
else
    subacc = [];
end

fprintf('    ----Development cost/cost_pure before training = %f / %f - %s\n', cost, cost_pure, datestr(now));
if length(subcost)>1
    fprintf('        ----subcosts = %f\n', subcost);
end
if length(subacc)>0
    fprintf('    ----(Sub)task accuracy = %2.2f%%\n', 100*subacc);
end

end


function rot = byMatrix(M,varargin)
% define rotations by matrices
%
% Syntax
%   M = eye(3)
%   rot = rotation.byMatrix(M)
%
% Input
%  M - 3x3 rotation matrix
%
% Output
%  rot - @rotation
%
% See also
% rotation/rotentation rotation/byEuler rotation/byAxisAngle

% negative determinant indicates improper rotation
isInv = false(size(M,3),1);
for i = 1:size(M,3)
  isInv(i) = det(M(:,:,i))<0;
end

% ensure M is proper
M(:,:,isInv) = -M(:,:,isInv);

rot = rotation(mat2quat(M));
rot.i = isInv;


function [boxes, labels] = imBoundingBox(img, varargin)
% Bounding box of regions within a 2D or 3D binary or label image.
%
%   BOX = imBoundingBox(IMG)
%   Compute the bounding boxes of the particles in labeled image IMG. If
%   the image is binary, one box, corresponding to the foreground (i.e.
%   the pixels with value 1) will be computed.
%
%   The result is a N-by-4 array BOX = [XMIN XMAX YMIN YMAX], containing
%   coordinates of the box extent.
%
%   The same result could be obtained with the regionprops function. The
%   advantage of using imBoundingBox is that equivalent boxes can be
%   obtained in one call.
%
%   BOX = imBoundingBox(IMG3D)
%   If input image is a 3D array, the result is a N-by-6 array, containing
%   the maximal coordinates in the X, Y and Z directions:
%   BOX = [XMIN XMAX YMIN YMAX ZMIN ZMAX].
%
%   [BOX, LABELS] = imBoundingBox(...)
%   Also returns the labels of the regions for which a bounding box was
%   computed. LABELS is a N-by-1 array with as many rows as BOX.
%
%   [...] = imBoundingBox(IMG, LABELS)
%   Specifies the labels of the regions whose bounding box need to be
%   computed.
%
%
%   Example
%   % Draw a complex particle together with its bounding box
%     img = imread('circles.png');
%     imshow(img); hold on;
%     boxes = imBoundingBox(img);
%     drawBox(boxes)
%
%   % Compute and display the bounding box of several particles
%     img = imread('rice.png');
%     img2 = img - imopen(img, ones(30, 30));
%     lbl = bwlabel(img2 > 50, 4);
%     boxes = imBoundingBox(lbl);
%     imshow(img); hold on;
%     drawBox(boxes, 'linewidth', 2, 'color', 'g');
%
%   See also
%     regionprops, drawBox, imOrientedBox, imEquivalentEllipse

% ------
% Author: David Legland
% e-mail: david.legland@inrae.fr
% Created: 2011-03-30,    using Matlab 7.9.0.529 (R2009b)
% Copyright 2011 INRA - Cepia Software Platform.

% History
% 2013-03-29 add support for 3D images

%% Process input arguments

% default spatial calibration
nd = ndims(img);
spacing = ones(1, nd);
origin  = ones(1, nd);
calib   = false;

% extract spacing
if ~isempty(varargin) && all(size(varargin{1}) == [1 nd])
    spacing = varargin{1};
    varargin(1) = [];
    calib = true;
    origin = zeros(1, nd);

    % extract origin
    if ~isempty(varargin) && all(size(varargin{1}) == [1 nd])
        origin = varargin{1};
        varargin(1) = [];
    end
end

% check if labels are specified
labels = [];
if ~isempty(varargin) && size(varargin{1}, 2) == 1
    labels = varargin{1};
end

%% Initialisations

% extract the set of labels, without the background
if isempty(labels)
    labels = imFindLabels(img);
end

if nd == 2
    %% Process planar case
    props = regionprops(img, 'BoundingBox');
    bb = reshape([props.BoundingBox], [4 length(props)])';
    bb = bb(labels, :);

    % convert to MatImage convention
    boxes = [bb(:, 1) bb(:, 1)+bb(:, 3) bb(:, 2) bb(:, 2)+bb(:, 4)];

    % spatial calibration
    if calib
        boxes = bsxfun(@plus, bsxfun(@times, (boxes - 1), spacing([1 1 2 2])), origin([1 1 2 2]));
    end

elseif nd == 3
    %% Process 3D case
    stats = regionprops3(img, 'BoundingBox');
    bb = reshape([stats.BoundingBox], [6 size(stats, 1)])';
    bb = bb(labels, :);

    % convert to MatImage convention
    boxes = [bb(:, 1) bb(:, 1)+bb(:, 4) bb(:, 2) bb(:, 2)+bb(:, 5) bb(:, 3) bb(:, 3)+bb(:, 6)];

    % spatial calibration
    if calib
        boxes = bsxfun(@plus, bsxfun(@times, (boxes - 1), spacing([1 1 2 2 3 3])), origin([1 1 2 2 3 3]));
    end
else
    error('Image dimension must be 2 or 3');
end


function grid = reshapeGrid(~, grid)
%RESHAPEGRID    Add the repeated endpoints in the lambda- and theta-directions
%and extract half of the points in the theta-direction.

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

% Note: We use periodic discretizations in longitude (lambda) and latitude
% (theta). These dicretizations do not include the repeated endpoints. Before
% plotting data with SPINOPERATOR/INITIALIZEMOVIE, we add these repeated
% endpoints to the grid, and extract half of the points in theta, those that
% correspond to [0, pi]. (We use the DFS method with doubled-up [-pi, pi]
% theta-grid.)

% Get the points:
ll = grid{1};
tt = grid{2};

% Get the number of grid points:
N = length(ll);

% Add the endpoints in lambda and theta:
ll = [ll, 2*ll(:,end) - ll(:,end-1)];
ll = [ll; ll(1,:)];
tt = [tt; 2*tt(end,:) - tt(end-1,:)];
tt = [tt, tt(:,1)];

% Extract half of the points in the theta-direction:
ll = ll(N/2+1:end,:);
tt = tt(N/2+1:end,:);

% Output the new grid:
grid{1} = ll;
grid{2} = tt;

end