function F = filmStrip( I, overlap, delta, border )
% Used to display R stacks of T images as a "filmstrip".
%
% See examples below to see what is meant by "filmstrip".
%
% USAGE
%  F = filmStrip( I, overlap, delta, border )
%
% INPUTS
%  I          - MxNxTxR or MxNx1xTxR or MxNx3xTxR array
%               (of bw or color images). R can equal 1.
%  overlap    - amount of overlap between successive frames
%  delta      - amount to shift each successive frame upward
%  border     - width of black border around each frame
%
% OUTPUTS
%  F       - filmstrip
%
% EXAMPLE - one filmstrip
%  load images;
%  F1 = filmStrip( video(:,:,1:15), 10, 2, 5 );   figure(1); im(F1); % one
%  F2 = filmStrip( videos(:,:,:,1:10), 5, 2, 3 ); figure(2); im(F2); % many
%
% See also MONTAGE2
%
% Piotr's Image&Video Toolbox      Version 2.0
% Copyright 2012 Piotr Dollar.  [pdollar-at-caltech.edu]
% Please email me if you find bugs, or have suggestions or questions!
% Licensed under the Simplified BSD License [see external/bsd.txt]

% convert I to be of type double, and have dimensions MxNxCxTxR
I = double(I); I = I/max(I(:)); sizI = size(I);
if(~any(sizI(3)==[1 3])); I=reshape(I,[sizI(1:2),1,sizI(3:end)]); end
I = padarray( I, [border border 0 0 0], 0, 'both' );
[mRows, nCols, nColor, nFrame, nStrip] = size(I);

% size of final filmstip object
sizF1 = [mRows+delta*(nFrame-1), nFrame*nCols-overlap*(nFrame-1), nColor];
sizF=sizF1;  sizF(1)=sizF1(1)*nStrip - ((nFrame-1-2)*delta)*(nStrip-1);
mRowsF1 = sizF1(1);

for i=1:nStrip

  % Create i-th film strip
  Fi = -ones( sizF1 );  row = 1;  col = sizF1(2);
  for f=nFrame:-1:1
    Fi( row:(row+mRows-1), (col-nCols+1):col, : ) = I(:,:,:,f,i);
    row = row + delta;  col = col - nCols + overlap;
  end

  % stop if creating single filmstrip
  if( nStrip==1 ); F=Fi; break; end

  % merge with the previous film strips
  if( i==1 ); F = -ones( sizF );  row2=1;  end
  Fc = F( row2:(row2+mRowsF1-1), : );
  locs=(Fc<0);  Fc(locs) = Fi(locs);
  F( row2:(row2+mRowsF1-1), :  ) = Fc;
  row2 = row2 + mRowsF1 - ((nFrame-1-2)*delta);
end
F(F<0)=1;


classdef TestDecomposeEssentialMat
    %TestDecomposeEssentialMat

    properties (Constant)
        fields = {'R1', 'R2', 't'};
    end

    methods (Static)
        function test_1
            E = [0 0 0; 0 0 -1; 0 1 0];
            S = cv.decomposeEssentialMat(E);
            validateattributes(S, {'struct'}, {'scalar'});
            assert(all(ismember(TestDecomposeEssentialMat.fields, fieldnames(S))));
            validateattributes(S.R1, {class(E)}, {'size',[3 3]});
            validateattributes(S.R2, {class(E)}, {'size',[3 3]});
            validateattributes(S.t, {class(E)}, {'vector', 'numel',3});
        end

        function test_error_argnum
            try
                cv.decomposeEssentialMat();
                throw('UnitTest:Fail');
            catch e
                assert(strcmp(e.identifier,'mexopencv:error'));
            end
        end
    end

end


function jed = epoch_to_jed_rd ( )

%*****************************************************************************80
%
%% EPOCH_TO_JED_RD returns the epoch of the RD calendar as a JED.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    15 June 2012
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Output, real JED, the Julian Ephemeris Date of the epoch.
%
  jed = 1721425.5;

  return
end


function table = l4vec_data_read ( input_filename,  n )

%*****************************************************************************80
%
%% L4VEC_DATA_READ reads data from an L4VEC file.
%
%  Discussion:
%
%    An L4VEC is a vector of L4's.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    03 December 2010
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string INPUT_FILENAME, the name of the input file.
%
%    Input, integer N, the number of columns in the data.
%
%    Output, integer TABLE(N), the data.
%
  table = zeros ( n, 1 );

  i = 0;

  while ( i < n )

    line = fgets ( input_unit );

    if ( line == -1 )
      fprintf ( 1, '\n' );
      fprintf ( 1, 'L4VEC_DATA_READ - Error!\n' );
      fprintf ( 1, '  End of input while reading data.\n' );
      error ( 'L4VEC_DATA_READ - Error!' );
    end

    if ( line(1) == '#' )

    elseif ( s_len_trim ( line ) == 0 )

    else

      i = i + 1;
      table(i) = s_to_l4 ( line );

    end

  end

  fclose ( input_unit );

  return
end


function [map,num,typ,scheme] = brewermap_view(N,scheme) %#ok<*ISMAT>
% An interactive figure for ColorBrewer colorscheme selection (RGB colormaps)
%
% (c) 2014-2022 Stephen Cobeldick
%
% View Cynthia Brewer's ColorBrewer colorschemes in a figure.
%
% * Two colorbars give the colorscheme in color and grayscale.
% * A button toggles between 3D-cube and 2D-lineplot of the RGB values.
% * A button toggles an endless demo cycle through the colorschemes.
% * A button reverses the colormap.
% * 35 buttons select any ColorBrewer colorscheme.
% * Text with the colorscheme's type (Diverging/Qualitative/Sequential)
% * Text with the colorscheme's number of nodes (defining colors).
%
%%% Syntax:
% brewermap_view
% brewermap_view(N)
% brewermap_view(N,scheme)
% brewermap_view([],...)
% brewermap_view(axes/figure handles,...) % see "Adjust Colormaps"
% [map,num,typ] = brewermap_view(...)
%
% Calling the function with an output argument blocks MATLAB execution until
% the figure is deleted: the final colormap and colorscheme are then returned.
%
%% Adjust Colormaps of Figures or Axes %%
%
% Only R2014b or later. Provide axes or figure handles as the first input
% and their colormaps will be updated in real-time by BREWERMAP_VIEW.
%
%%% Example:
%
% >> S = load('spine');
% >> image(S.X)
% >> brewermap_view(gca)
%
%% Input and Output Arguments %%
%
%%% Inputs (*=default):
% N = NumericScalar, an integer to define the colormap length.
%   = *[], colormap length of two hundred and fifty-six (256).
%   = NaN, same length as the defining RGB nodes (useful for Line ColorOrder).
%   = Array of axes/figure handles. R2014b or later only.
% scheme = CharRowVector or StringScalar, a ColorBrewer colorscheme name.
%
%%% Outputs (these block code execution until the figure is closed!):
% map = NumericMatrix, the colormap defined when the figure is closed.
% num = NumericVector, the number of nodes defining the ColorBrewer colorscheme.
% typ = CharRowVector, the colorscheme type: 'Diverging'/'Qualitative'/'Sequential'.
%
% See also BREWERMAP BREWERMAP_PLOT CUBEHELIX PRESET_COLORMAP MAXDISTCOLOR
% RGBPLOT COLORMAP COLORMAPEDITOR COLORBAR UICONTROL ADDLISTENER

%% Input Wrangling %%
%
persistent ax2D ln2D ax3D pt3D txtH is2D cbAx cbIm pTxt pSld bEig bGrp bRev scm isr
%
new = isempty(ax2D)||~ishghandle(ax2D);
dfn = 256;
upd = false;
upb = false;
hgv = [];
nmr = dfn;
%
err = 'First input must be a real positive scalar numeric or [] or NaN.';
if nargin==0 || isnumeric(N)&&isequal(N,[])
	N = dfn;
elseif isnumeric(N)
	assert(isscalar(N),'SC:brewermap_view:NotScalarNumeric',err)
	assert(isnan(N)||isreal(N)&&isfinite(N)&&fix(N)==N&&N>0,...
		'SC:brewermap_view:NotRealPositiveNotNaN',err)
	N = double(N);
elseif all(ishghandle(N(:))) % R2014b or later
	assert(isgraphics(N(:),'axes')|isgraphics(N(:),'figure'),...
		'SC:brewermap_view:NotAxesNorFigureHandles',...
		'First input may be an array of figure or axes handles.')
	hgv = N(:);
	nmr = arrayfun(@(h)size(colormap(h),1),hgv);
	N = nmr(1);
else
	error('SC:brewermap_view:UnsupportedInput',err)
end
%
[mcs,nmn,pyt] = brewermap('list');
%
% Check BREWERMAP output:
tmp = find([any(diff(double(char(pyt)),1),2);1]);
assert(isequal(tmp,[9;17;35]),'SC:brewermap_view:SchemeSequence',...
	'The BREWERMAP function returned an unexpected scheme sequence.')
%
% Default pseudo-random colorscheme:
if nargin==0 || new
	isr = rand(1)>0.5;
	scm = mcs{1+mod(round(now*1e7),numel(mcs))};
end
%
% Parse input colorscheme:
if nargin==2
	scheme = bmv1s2c(scheme);
	assert(ischar(scheme) && ndims(scheme)==2 && size(scheme,1)==1,...
		'SC:brewermap_view:NotCharacterVector',...
		'Second input <scheme> must be a 1xN char vector.')
	% Check if a reversed colormap was requested:
	isr = strncmp(scheme,'-',1) || strncmp(scheme,'*',1);
	isd = strncmp(scheme,'+',1) || isr;
	scm = scheme(1+isd:end);
end
%
if isnan(N)
	N = nmn(strcmpi(scm,mcs));
end
%
%% Ensure Figure Exists %%
%
% LHS and RHS slider bounds/limits, and slider step sizes:
lbd = 1;
rbd = dfn;
stp = [1,10]; % [minor,major]
%
% Define the 3D cube axis order:
xyz = 'RGB'; % choose order
[~,xyz] = ismember(xyz,'RGB');
%
if new % Create a new figure.
	%
	% Figure parameters:
	M = 9; % buttons per column
	gap = 0.01; % gaps
	bth = 0.04; % demo button height
	btw = 0.10; % demo button width
	bgh = 0.40; % button group height
	cbw = 0.23; % colorbar width (both together)
	cbh = 1-3*gap-bth; % colorbar height
	axh = 1-bgh-2*gap; % axes height
	axw = 1-cbw-2*gap; % axes width
	bgw = axw-btw-gap; % button group width
	%
	figH = figure('HandleVisibility','callback', 'Color','white',...
		'IntegerHandle','off', 'NumberTitle','off', 'Units','normalized',...
		'Name','ColorBrewer Interactive ColorScheme Selector',...
		'MenuBar','figure', 'Toolbar','none', 'Tag',mfilename);
	%
	% Add 2D lineplot:
	ax2D = axes('Parent',figH, 'Position',[gap,bgh+gap,axw,axh], 'Box','on',...
		'ColorOrder',[1,0,0; 0,1,0; 0,0,1; 0.6,0.6,0.6], 'HitTest','off',...
		'Visible','off', 'XLim',[0,1], 'YLim',[0,1], 'XTick',[], 'YTick',[]);
	ln2D = line([0,0,0,0;1,1,1,1],[0,0,0,0;1,1,1,1], 'Parent',ax2D,...
		'Visible','off', 'Linestyle','-', 'Marker','.');
	%
	% Add 3D scatterplot:
	ax3D = axes('Parent',figH, 'OuterPosition',[0,bgh,axw+2*gap,1-bgh],...
		'Visible','on', 'XLim',[0,1], 'YLim',[0,1], 'ZLim',[0,1], 'HitTest','on');
	pt3D = patch('Parent',ax3D, 'XData',[0;1], 'YData',[0;1], 'ZData',[0;1],...
		'Visible','on', 'LineStyle','none', 'FaceColor','none', 'MarkerEdgeColor','none',...
		'Marker','o', 'MarkerFaceColor','flat', 'MarkerSize',10, 'FaceVertexCData',[1,1,0;1,0,1]);
	view(ax3D,3);
	grid(ax3D,'on')
	lbl = {'Red','Green','Blue'};
	xlabel(ax3D,lbl{xyz(1)})
	ylabel(ax3D,lbl{xyz(2)})
	zlabel(ax3D,lbl{xyz(3)})
	%
	% Add warning text:
	txtH = text('Parent',ax2D, 'Units','normalized', 'Position',[1,1],...
		'HorizontalAlignment','right', 'VerticalAlignment','top', 'Color','k');
	%
	% Add demo button:
	demo = uicontrol(figH, 'Style','togglebutton', 'Units','normalized',...
		'Position',[1-cbw/2,1-bth-gap,cbw/2-gap,bth], 'String','Demo',...
		'Max',1, 'Min',0, 'Callback',@bmvDemo); %#ok<NASGU>
	% Add 2D/3D button:
	is2D = uicontrol(figH, 'Style','togglebutton', 'Units','normalized',...
		'Position',[1-cbw/1,1-bth-gap,cbw/2-gap,bth], 'String','2D / 3D',...
		'Max',1, 'Min',0, 'Callback',@bmv2D3D);
	% Add reverse button:
	bRev = uicontrol(figH, 'Style','togglebutton', 'Units','normalized',...
		'Position',[bgw+2*gap,bgh-bth,btw,bth], 'String','Reverse',...
		'Max',1, 'Min',0, 'Callback',@bmvRevM);
	%
	% Add colorbars:
	C(1,1,:) = [1,1,1];
	cbAx(2) = axes('Parent',figH, 'Visible','off', 'Units','normalized',...
		'Position',[1-cbw/2,gap,cbw/2-gap,cbh], 'YLim',[0.5,1.5],...
		'YDir','reverse', 'HitTest','off');
	cbAx(1) = axes('Parent',figH, 'Visible','off', 'Units','normalized',...
		'Position',[1-cbw/1,gap,cbw/2-gap,cbh], 'YLim',[0.5,1.5],...
		'YDir','reverse', 'HitTest','off');
	cbIm(2) = image('Parent',cbAx(2), 'CData',C);
	cbIm(1) = image('Parent',cbAx(1), 'CData',C);
	%
	% Add parameter slider, listener, and corresponding text:
	sv = mean([lbd,rbd],2);
	pTxt = uicontrol(figH,'Style','text', 'Units','normalized',...
		'Position',[bgw+2*gap,bgh-2*bth-gap,btw,bth], 'String','X');
	pSld = uicontrol(figH,'Style','slider', 'Units','normalized',...
		'Position',[bgw+2*gap,gap,btw,bgh-2*bth-gap], 'Min',lbd(1), 'Max',rbd(1),...
		'SliderStep',stp(1,:)/(rbd(1)-lbd(1)), 'Value',sv(1));
	addlistener(pSld, 'Value', 'PostSet',@bmvSldr);
	%
	% Add colorscheme button group:
	bGrp = uibuttongroup('Parent',figH, 'BorderType','none', 'Units','normalized',...
		'BackgroundColor','white', 'Position',[gap,gap,bgw,bgh-gap]);
	% Determine button locations:
	Z = 1:numel(mcs);
	Z = Z+(Z>17);
	C = (ceil(Z/M)-1)/4;
	R = (M-1-mod(Z-1,M))/M;
	% Add colorscheme buttons to group:
	for jj = numel(mcs):-1:1
		bEig(jj) = uicontrol('Parent',bGrp, 'Style','Toggle', 'String',mcs{jj},...
			'Unit','normalized', 'Position',[C(jj),R(jj),1/4,1/M]);
	end
	set(bGrp,'SelectionChangeFcn',@bmvChgS);
	%
end
%
%% Nested Functions %%
%
	function str = bmvName()
		% Generate the colorscheme name.
		drn = '+-';
		ids = strcmpi(scm,mcs);
		if any(ids)
			str = [drn(1+isr),mcs{ids}];
		else % will throw an error in BREWERMAP:
			str = scm;
		end
	end
%
	function bmvUpDt()
		% Update all graphics objects in the figure.
		%
		% Get ColorBrewer colormap and grayscale equivalent:
		[map,num,typ] = brewermap(N,bmvName());
		mag = map*[0.298936;0.587043;0.114021];
		%
		% Update colorbar values:
		set(cbAx, 'YLim', [0,abs(N)+(N==0)]+0.5);
		set(cbIm(1), 'CData',reshape(map,[],1,3))
		set(cbIm(2), 'CData',repmat(mag,[1,1,3]))
		%
		% Update 2D line / 3D patch values:
		if  get(is2D, 'Value') % 2D
			set(ln2D, 'XData',linspace(0,1,abs(N)));
			set(ln2D,{'YData'},num2cell([map,mag],1).');
		else % 3D
			set(pt3D,...
				'XData',map(:,xyz(1)),...
				'YData',map(:,xyz(2)),...
				'ZData',map(:,xyz(3)),...
				'FaceVertexCData',map)
		end
		%
		% Update reverse button:
		set(bRev, 'Value',isr)
		%
		% Update warning text:
		str = {[bmvName(),' '];[typ,' '];sprintf('%d Nodes ',num)};
		set(txtH,'String',str);
		%
		% Update parameter value text:
		set(pTxt(1), 'String',sprintf('N = %.0f',N));
		%
		% Update external axes/figure:
		nmr(1) = N;
		for k = 1:numel(hgv)
			colormap(hgv(k),brewermap(nmr(k),bmvName()));
		end
		%
		drawnow()
	end
%
	function bmv2D3D(h,~)
		% Switch between 2D-line and 3D-cube representation.
		%
		if get(h,'Value') % 2D
			set(ax3D, 'HitTest','off', 'Visible','off')
			set(ax2D, 'HitTest','on',  'Visible','on')
			set(pt3D, 'Visible','off')
			set(ln2D, 'Visible','on')
		else % 3D
			set(ax2D, 'HitTest','off', 'Visible','off')
			set(ax3D, 'HitTest','on',  'Visible','on')
			set(ln2D, 'Visible','off')
			set(pt3D, 'Visible','on')
		end
		%
		bmvUpDt();
	end
%
	function bmvChgS(~,e)
		% Change the colorscheme.
		%
		scm = get(e.NewValue,'String');
		%
		bmvUpDt()
	end
%
	function bmvRevM(h,~)
		% Reverse the colormap.
		%
		isr = logical(get(h,'Value'));
		%
		bmvUpDt()
	end
%
	function bmvSldr(~,~)
		% Update the slider position.
		%
		if ~upd
			return
		end
		%
		N = round(get(pSld,'Value'));
		%
		bmvUpDt()
	end
%
	function bmvDemo(h,~)
		% Display all ColorBrewer colorschemes sequentially.
		%
		cnt = 0;
		while ishghandle(h)&&get(h,'Value')
			cnt = mod(cnt+1,pow2(53));
			%
			if mod(cnt,23)<1
				ids = 1+mod(find(strcmpi(scm,mcs)),numel(mcs));
				scm = mcs{ids};
				try %#ok<TRYNC>
					set(bGrp, 'SelectedObject',bEig(ids));
				end
			end
			%
			if mod(cnt,69)<1
				isr = ~isr;
			end
			%
			upb = (upb || N<=1) && N<dfn;
			N = N - 1 + 2*upb;
			%
			% Update slider position:
			upd = false;
			try %#ok<TRYNC>
				set(pSld, 'Value',N)
				bmvUpDt()
			end
			upd = true;
			%
			% Faster/slower:
			pause(0.1);
		end
		%
	end
%
%% Initialize the Figure %%
%
set(bGrp,'SelectedObject',bEig(strcmpi(scm,mcs)));
set(pSld,'Value',max(lbd,min(rbd,N)));
upd = true;
bmvUpDt()
%
if nargout
	waitfor(ax2D);
	scheme = bmvName();
else
	clear map
end
%
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%brewermap_view
function arr = bmv1s2c(arr)
% If scalar string then extract the character vector, otherwise data is unchanged.
if isa(arr,'string') && isscalar(arr)
	arr = arr{1};
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%bmv1s2c
%
% Copyright (c) 2014-2022 Stephen Cobeldick
%
% Licensed under the Apache License, Version 2.0 (the "License");
% you may not use this file except in compliance with the License.
% You may obtain a copy of the License at
%
% http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS,
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
% See the License for the specific language governing permissions and limitations under the License.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%license


% CVX: Top-level commands to control CVX.
%   cvx_begin         - Starts a new CVX specification.
%   cvx_begin_set     - Starts a new cvx CVX specification.
%   cvx_cd            - Change current working directory to a CVX subdirectory.
%   cvx_clear         - Clears all active CVX data.
%   cvx_end           - Completes a cvx specification.
%   cvx_end_set       - Completes a cvx set specification.
%   cvx_gp_precision  - Changes the precision of CVX's GP approximation.
%   cvx_pause         - Pauses the processing of CVX models.
%   cvx_power_warning - Controls the CVX warning message for x.^p expressions.
%   cvx_precision     - Controls CVX solver precision.
%   cvx_profile       - CVX-specific profiler control.
%   cvx_quiet         - CVX output control.
%   cvx_solver        - CVX solver selection.
%   cvx_where         - Returns the location of the CVX system.

% Copyright 2005-2016 CVX Research, Inc.
% See the file LICENSE.txt for full copyright information.
% The command 'cvx_where' will show where this file is located.


%FILLPOLY  Fills the area bounded by one or more polygons
%
%     img = cv.fillPoly(img, pts)
%     [...] = cv.fillPoly(..., 'OptionName', optionValue, ...)
%
% ## Input
% * __img__ Image.
% * __pts__ Array of polygons where each polygon is represented as an array
%   of points. A cell array of cell arrays of 2-element vectors, in the form:
%   `{{[x,y], [x,y], ...}, ...}`, or a cell array of Nx2 matrices.
%
% ## Output
% * __img__ Output image, same size and type as input `img`.
%
% ## Options
% * __Color__ 3-element floating-point vector specifying polygon color.
%   default zeros
% * __LineType__ Type of the polygon boundaries. One of:
%   * __4__ 4-connected line
%   * __8__ 8-connected line (default)
%   * __AA__ anti-aliased line
% * __Shift__ Number of fractional bits in the vertex coordinates. default 0
% * __Offset__ Optional offset of all points of the contours. default `[0,0]`
%
% The function cv.fillPoly fills an area bounded by several polygonal
% contours. The function can fill complex areas, for example, areas with
% holes, contours with self-intersections (some of thier parts), and so forth.
%
% See also: cv.line, cv.fillConvexPoly, roipoly, poly2mask
%


% [INPUT]
% file = A string representing the full path to the Excel spreadsheet containing the dataset.
% version = A string representing the version of the dataset.
% date_format_base = A string representing the base date format used in the Excel spreadsheet for all elements except balance sheet ones (optional, default='dd/mm/yyyy').
% date_format_balance = A string representing the date format used in the Excel spreadsheet for balance sheet elements (optional, default='QQ yyyy').
% shares_type = A string representing the type of data included in the Shares sheet (optional, default='P'):
%   - 'P' for prices;
%   - 'R' for returns.
% crises_type = A string representing the type of data included in the Crises sheet (optional, default='R'):
%   - 'E' for events;
%   - 'R' for time ranges.
% distress_threshold = A float [0.05,0.75] representing the threshold at which distress statuses are detected (optional, default=0.05).
%
% [OUTPUT]
% ds = A structure containing the parsed dataset.

function ds = parse_dataset(varargin)

    persistent ip;

    if (isempty(ip))
        ip = inputParser();
        ip.addRequired('file',@(x)validateattributes(x,{'char'},{'nonempty' 'size' [1 NaN]}));
        ip.addRequired('version',@(x)validateattributes(x,{'char'},{'nonempty' 'size' [1 NaN]}));
        ip.addOptional('date_format_base','dd/mm/yyyy',@(x)validateattributes(x,{'char'},{'nonempty' 'size' [1 NaN]}));
        ip.addOptional('date_format_balance','QQ yyyy',@(x)validateattributes(x,{'char'},{'nonempty' 'size' [1 NaN]}));
        ip.addOptional('shares_type','P',@(x)any(validatestring(x,{'P' 'R'})));
        ip.addOptional('crises_type','R',@(x)any(validatestring(x,{'E' 'R'})));
        ip.addOptional('distress_threshold',0.05,@(x)validateattributes(x,{'double'},{'real' 'finite' '>=' 0.05 '<=' 0.75 'scalar'}));
    end

    ip.parse(varargin{:});

    ipr = ip.Results;
    file = ipr.file;
    version = validate_version(ipr.version);
    date_format_base = validate_date_format(ipr.date_format_base,true);
    date_format_balance = validate_date_format(ipr.date_format_balance,false);
    shares_type = ipr.shares_type;
    crises_type = ipr.crises_type;
    distress_threshold = ipr.distress_threshold;

    file_sheets = validate_xls(ipr.file,'D');

    nargoutchk(1,1);

    ds = parse_dataset_internal(file,file_sheets,version,date_format_base,date_format_balance,shares_type,crises_type,distress_threshold);

end

function ds = parse_dataset_internal(file,file_sheets,version,date_format_base,date_format_balance,shares_type,crises_type,distress_threshold)

    [~,file_name,file_ext] = fileparts(file);
    file_name = [file_name file_ext];

    if (~strcmp(file_sheets{1},'Shares'))
        error(['Error in dataset ''' file_name ''': the first sheet must be the ''Shares'' one.']);
    end

    file_sheets_other = file_sheets(2:end);

    using_prices = strcmp(shares_type,'P');

    if (using_prices)
        tab_shares = parse_table_standard(file,file_name,1,'Shares',date_format_base,[],[],true);
    else
        tab_shares = parse_table_standard(file,file_name,1,'Shares',date_format_base,[],[],false);
    end

    if (any(any(ismissing(tab_shares))))
        error(['Error in dataset ''' file_name ''': the ''Shares'' sheet contains invalid or missing values.']);
    end

    if (width(tab_shares) < 5)
        error(['Error in dataset ''' file_name ''': the ''Shares'' sheet must contain at least the following elements: the observations dates and the time series of the benchmark and at least 3 firms.']);
    end

    n = width(tab_shares) - 2;

    if (using_prices)
        t = height(tab_shares);

        if (t < 253)
            error(['Error in dataset ''' file_name ''': the ''Shares'' sheet must contain at least 253 observations (a full business year plus an additional observation at the beginning of the time series) in order to run consistent calculations.']);
        end

        t = t - 1;
    else
        t = height(tab_shares);

        if (t < 252)
            error(['Error in dataset ''' file_name ''': the ''Shares'' sheet must contain at least 252 observations (a full business year) in order to run consistent calculations.']);
        end
    end

    dates_num = datenum(tab_shares{:,1}); %#ok<DATNM>
    tab_shares.Date = [];

    if (using_prices)
        tab_shares_headers = strtrim(tab_shares.Properties.VariableNames);

        prices = table2array(tab_shares);
        dlp = diff(log(prices));
        dlp(~isfinite(dlp)) = 0;
        prices = prices(:,2:end);

        index_name = tab_shares_headers{1};
        firm_names = tab_shares_headers(2:end);

        index = dlp(:,1);
        returns = dlp(:,2:end);
    else
        tab_shares_headers = strtrim(tab_shares.Properties.VariableNames);

        prices = [];

        index_name = tab_shares_headers{1};
        firm_names = tab_shares_headers(2:end);

        index = tab_shares{:,1};
        returns = tab_shares{:,2:end};
    end

    if (any(cellfun(@isempty,regexpi(firm_names,'^[A-Z][A-Z0-9]{0,11}$'))))
        error(['Error in dataset ''' file_name ''': the ''Shares'' sheet contains invalid firm names (containing invalid characters, not starting with a letter and/or greater than 12 characters).']);
    end

    volumes = [];
    capitalizations = [];

    risk_free_rate = [];
    cds = [];

    assets = [];
    equity = [];
    separate_accounts = [];

    state_variables = [];
    state_variables_names = [];

    groups = 0;
    group_delimiters = [];
    group_names = [];
    group_short_names = [];

    crises = 0;
    crises_dummy = [];
    crisis_dates = [];
    crisis_dummies = [];
    crisis_names = [];

    for tab = {'Volumes' 'Capitalizations' 'CDS' 'Assets' 'Equity' 'Separate Accounts' 'State Variables' 'Groups' 'Crises'}

        tab_index = find(strcmp(file_sheets_other,tab),1);

        if (isempty(tab_index))
            continue;
        end

        tab_index = tab_index + 1;
        tab_name = char(tab);

        switch (tab_name)

            case 'Volumes'

                tab_volumes = parse_table_standard(file,file_name,tab_index,tab_name,date_format_base,dates_num,firm_names,true);
                volumes = table2array(tab_volumes);

            case 'Capitalizations'

                tab_capitalizations = parse_table_standard(file,file_name,tab_index,tab_name,date_format_base,dates_num,firm_names,true);
                capitalizations = table2array(tab_capitalizations);

            case 'CDS'

                tab_cds = parse_table_standard(file,file_name,tab_index,tab_name,date_format_base,dates_num,[{'RF'} firm_names],true);
                tab_arr = table2array(tab_cds);
                risk_free_rate = tab_arr(:,1);
                cds = tab_arr(:,2:end) ./ 10000;

            case 'Assets'

                tab_assets = parse_table_balance(file,file_name,tab_index,tab_name,date_format_balance,dates_num,firm_names,true);
                assets = table2array(tab_assets);

            case 'Equity'

                tab_equity = parse_table_balance(file,file_name,tab_index,tab_name,date_format_balance,dates_num,firm_names,false);
                equity = table2array(tab_equity);

            case 'Separate Accounts'

                tab_separate_accounts = parse_table_balance(file,file_name,tab_index,tab_name,date_format_balance,dates_num,firm_names,true);
                separate_accounts = table2array(tab_separate_accounts);

            case 'State Variables'

                tab_state_variables = parse_table_standard(file,file_name,tab_index,tab_name,date_format_base,dates_num,[],false);
                state_variables = table2array(tab_state_variables);
                state_variables_names = tab_state_variables.Properties.VariableNames;

             case 'Groups'

                [tab_groups,group_counts] = parse_table_groups(file,file_name,tab_index,tab_name,firm_names);
                groups = height(tab_groups);
                group_delimiters = cumsum(group_counts(1:end-1,:));
                group_names = tab_groups{:,1};
                group_short_names = tab_groups{:,2};

             case 'Crises'

                if (strcmp(crises_type,'E'))
                    [tab_crises,crises_dummy] = parse_table_crises_dates(file,file_name,tab_index,tab_name,date_format_base,dates_num);
                    crises = height(tab_crises);
                    crisis_dates = datenum(tab_crises{:,1}); %#ok<DATNM>
                    crisis_names = tab_crises{:,2};
                else
                    [tab_crises,crisis_dummies,crises_dummy] = parse_table_crises_ranges(file,file_name,tab_index,tab_name,date_format_base,dates_num);
                    crises = height(tab_crises);
                    crisis_dates = [datenum(tab_crises{:,2}) datenum(tab_crises{:,3})]; %#ok<DATNM>
                    crisis_names = tab_crises{:,1};
                end

        end
    end

    if (using_prices)
        dates_num = remove_first_observation(dates_num);
        prices = remove_first_observation(prices);
        volumes = remove_first_observation(volumes);
        capitalizations = remove_first_observation(capitalizations);
        risk_free_rate = remove_first_observation(risk_free_rate);
        cds = remove_first_observation(cds);
        assets = remove_first_observation(assets);
        equity = remove_first_observation(equity);
        separate_accounts = remove_first_observation(separate_accounts);
        state_variables = remove_first_observation(state_variables);
        crises_dummy = remove_first_observation(crises_dummy);
        crisis_dummies = remove_first_observation(crisis_dummies);
    end

    [dates_year,~,~,~,~,~] = datevec(dates_num);

    if (~isempty(assets) && ~isempty(equity))
        liabilities = assets - equity;
    else
        liabilities = [];
    end

    if (strcmp(crises_type,'R') && ~isempty(crises_dummy))
        crisis_dates(:,1) = max(crisis_dates(:,1),dates_num(1));
        crisis_dates(:,2) = min(crisis_dates(:,2),dates_num(end));
    end

    if (isempty(prices))
        [defaults,insolvencies] = detect_distress(distress_threshold,returns,volumes,capitalizations,cds,equity);
    else
        [defaults,insolvencies] = detect_distress(distress_threshold,prices,volumes,capitalizations,cds,equity);
    end

    dd_limit = ceil(n * 0.25);
    defaults_cnt = sum(~isnan(defaults));
    insolvencies_cnt = sum(~isnan(insolvencies));

    if (any(defaults < 63))
        error(['Error in dataset ''' file_name ''': it contains firms that defaulted too early and must be removed.']);
    end

    if (defaults_cnt > dd_limit)
        error(['Error in dataset ''' file_name ''': it contains too many defaulted firms (' num2str(defaults_cnt) ' out of a maximum of ' num2str(dd_limit) ').']);
    end

    if (any(insolvencies < 63))
        error(['Error in dataset ''' file_name ''': it contains firms that became insolvent too early and must be removed.']);
    end

    if (insolvencies_cnt > dd_limit)
        error(['Error in dataset ''' file_name ''': it contains too many insolvent firms (' num2str(insolvencies_cnt) ' out of a maximum of ' num2str(dd_limit) ').']);
    end

    includes_volumes = ismember({'Volumes'},file_sheets_other);
    includes_capitalizations = ismember({'Capitalizations'},file_sheets_other);
    includes_cds = ismember({'CDS'},file_sheets_other);
    includes_balance_sheet = sum(ismember({'Assets' 'Equity'},file_sheets_other)) == 2;
    includes_crises = ~isempty(crises_dummy);

    if (using_prices && includes_capitalizations)
        supports_bubbles_detection = true;
    else
        supports_bubbles_detection = false;
        warning('MATLAB:SystemicRisk',['The dataset ''' file_name ''' does not meet all the requirements for the computation of bubbles detection measures (''Shares'' must be expressed as prices, the ''Capitalizations'' sheet must be included).']);
    end

    if (includes_cds)
        supports_cross_entropy = true;
    else
        supports_cross_entropy = false;
        warning('MATLAB:SystemicRisk',['The dataset ''' file_name ''' does not meet all the requirements for the computation of cross-entropy measures (the ''CDS'' sheet must be included).']);
    end

    if (includes_capitalizations && includes_balance_sheet)
        supports_cross_sectional = true;
    else
        supports_cross_sectional = false;
        warning('MATLAB:SystemicRisk',['The dataset ''' file_name ''' does not meet all the requirements for the computation of cross-sectional measures (''Assets'', ''Capitalizations'' and ''Equity'' sheets must be included).']);
    end

    if (includes_capitalizations && includes_cds && includes_balance_sheet)
        supports_default = true;
    else
        supports_default = false;
        warning('MATLAB:SystemicRisk',['The dataset ''' file_name ''' does not meet all the requirements for the computation of default measures (''Assets'', ''Capitalizations'', ''CDS'' and ''Equity'' sheets must be included).']);
    end

    if (using_prices && includes_volumes && includes_capitalizations)
        supports_liquidity = true;
    else
        supports_liquidity = false;
        warning('MATLAB:SystemicRisk',['The dataset ''' file_name ''' does not meet all the requirements for the computation of liquidity measures (''Shares'' must be expressed as prices, ''Capitalizations'' and ''Volumes'' sheets must be included).']);
    end

    if (includes_crises)
        supports_comparison = true;
    else
        supports_comparison = false;
        warning('MATLAB:SystemicRisk',['The dataset ''' file_name ''' does not meet all the requirements for the comparison of systemic risk measures (the ''Crises'' sheet must be included).']);
    end

    returns = distress_data(returns,defaults);
    prices = distress_data(prices,defaults);
    volumes = distress_data(volumes,defaults);
    capitalizations = distress_data(capitalizations,defaults);
    cds = distress_data(cds,defaults);
    assets = distress_data(assets,defaults);
    equity = distress_data(equity,defaults);
    liabilities = distress_data(liabilities,defaults);
    separate_accounts = distress_data(separate_accounts,defaults);

    ds = struct();

    ds.File = file;
    ds.Version = version;
    ds.CreationDate = now(); %#ok<TNOW1>

    ds.Result = [];
    ds.ResultDate = [];
    ds.ResultAnalysis = [];
    ds.ResultSerial = [];

    ds.TimeSeries = {'Assets' 'Capitalizations' 'CDS' 'Equity' 'Liabilities' 'Returns' 'SeparateAccounts' 'Volumes'};

    ds.N = n;
    ds.T = t;

    ds.DatesNum = dates_num;
    ds.DatesStr = cellstr(datestr(datetime(dates_num,'ConvertFrom','datenum'),date_format_base)); %#ok<DATST>
    ds.MonthlyTicks = numel(unique(dates_year)) <= 3;

    ds.IndexName = index_name;
    ds.FirmNames = firm_names;

    ds.Index = index;
    ds.Returns = returns;

    ds.Prices = prices;
    ds.Volumes = volumes;
    ds.Capitalizations = capitalizations;

    ds.RiskFreeRate = risk_free_rate;
    ds.CDS = cds;

    ds.Assets = assets;
    ds.Equity = equity;
    ds.Liabilities = liabilities;
    ds.SeparateAccounts = separate_accounts;

    ds.StateVariables = state_variables;
    ds.StateVariablesNames = state_variables_names;

    ds.Groups = groups;
    ds.GroupDelimiters = group_delimiters;
    ds.GroupNames = group_names;
    ds.GroupShortNames = group_short_names;

    ds.Crises = crises;
    ds.CrisesType = crises_type;
    ds.CrisesDummy = crises_dummy;
    ds.CrisisDates = crisis_dates;
    ds.CrisisDummies = crisis_dummies;
    ds.CrisisNames = crisis_names;

    ds.Defaults = defaults;
    ds.Insolvencies = insolvencies;

    ds.SupportsBubblesDetection = supports_bubbles_detection;
    ds.SupportsComponent = true;
    ds.SupportsConnectedness = true;
    ds.SupportsCrossEntropy = supports_cross_entropy;
    ds.SupportsCrossQuantilogram = true;
    ds.SupportsCrossSectional = supports_cross_sectional;
    ds.SupportsDefault = supports_default;
    ds.SupportsLiquidity = supports_liquidity;
    ds.SupportsRegimeSwitching = true;
    ds.SupportsSpillover = true;
    ds.SupportsTailDependence = true;
    ds.SupportsComparison = supports_comparison;

end

%% VALIDATION

function date_format = validate_date_format(date_format,base)

    try
        datestr(now(),date_format); %#ok<DATST,TNOW1>
    catch e
        error(['The date format ''' date_format ''' is invalid.' new_line() strtrim(regexprep(e.message,'Format.+$',''))]);
    end

    if (~any(regexp(date_format,'(?<!y)(y{2}|y{4})(?!y)')))
        error('The date format must always include year information.');
    end

    if (any(regexp(date_format,'(?:HH|MM|SS|FFF|AM|PM)')))
        error('The date format must not include time information.');
    end

    if (base && ~any(regexp(date_format,'(?<!d)(d{2}|d{4})(?!d)')))
        error('The base date format must be tied to a daily frequency.');
    end

end

function version = validate_version(version)

    if (~any(regexp(version,'v\d+\.\d+')))
        error('The version format is invalid.');
    end

end

%% PARSING

function tab = ensure_field_consistency(file_name,name,tab,index,output_type,target_type,date_format_dt)

    switch (target_type)

        case 'char'

            if (strcmp(output_type,'cell'))
                field = vertcat(tab{:,index});

                if (~ischar(field{1}))
                    error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains invalid column types.']);
                end

                tab.(tab.Properties.VariableNames{index}) = field;
            elseif (strcmp(output_type,'char'))
                tab.(tab.Properties.VariableNames{index}) = cellstr(tab{:,index});
            else
                error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains invalid column types.']);
            end

        case 'datetime'

            if (strcmp(output_type,'cell'))
                if (iscell(tab{1,index}))
                    field = cellfun(@(x)x,tab{:,index},'UniformOutput',false);
                end

                if (ischar(field{1}))
                    field = cellfun(@(x)datetime(x,'InputFormat',date_format_dt),field,'UniformOutput',false);
                elseif (~isdatetime(field{1}))
                    error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains invalid column types.']);
                end

                tab.(tab.Properties.VariableNames{index}) = [field{:}].';
            elseif (strcmp(output_type,'char'))
                field = cellfun(@(x)datetime(x,'InputFormat',date_format_dt),cellstr(tab{:,index}));
                tab.(tab.Properties.VariableNames{1}) = field;
            elseif (~strcmp(output_type,'datetime'))
                error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains invalid column types.']);
            end

        case 'double'

            if (strcmp(output_type,'cell'))
                if (iscell(tab{1,index}))
                    field = cellfun(@(x)x{:},tab{:,index},'UniformOutput',false);
                end

                field_first = field{1};

                if (~isa(field_first,'double') || ~isscalar(field_first))
                    error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains invalid column types.']);
                end

                tab.(tab.Properties.VariableNames{index}) = [field{:}].';
            elseif (~strcmp(output_type,'double'))
                error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains invalid column types.']);
            end

        otherwise

            error(['Invalid target type specified: ' target_type '.']);

    end

end

function tab = parse_table_balance(file,file_name,index,name,date_format,dates_num,firm_names,check_negatives)

    data_types = [{'datetime'} repmat({'double'},1,numel(firm_names))];
    tab_partial = read_table(file,file_name,index,name,date_format,data_types);

    if (~strcmp(tab_partial.Properties.VariableNames(1),'Date'))
        error(['Error in dataset ''' file_name ''': the first column of the ''' name ''' sheet must be called ''Date'' and must contain the observation dates.']);
    end

    if (any(any(~isfinite(tab_partial{:,2:end}))))
        error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains invalid values.']);
    end

    if (check_negatives && any(any(tab_partial{:,2:end} < 0)))
        error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains negative values.']);
    end

    t_current = height(tab_partial);
    dates_num_current = datenum(tab_partial.Date); %#ok<DATNM>
    tab_partial.Date = [];

    if (t_current ~= numel(unique(dates_num_current)))
        error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains duplicate observation dates.']);
    end

    if (any(dates_num_current ~= sort(dates_num_current)))
        error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains unsorted observation dates.']);
    end

    if (t_current ~= numel(unique(cellstr(datestr(dates_num,date_format))))) %#ok<DATST>
        error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains an invalid number of observation dates.']);
    end

    if (~isequal(tab_partial.Properties.VariableNames,firm_names))
        error(['Error in dataset ''' file_name ''': the firm names between the ''Shares'' sheet and the ''' name ''' sheet are mismatching.']);
    end

    dates_from = cellstr(datestr(dates_num_current,date_format)).'; %#ok<DATST>
    dates_to = cellstr(datestr(dates_num,date_format)).'; %#ok<DATST>

    if (any(~ismember(dates_to,dates_from)))
        error(['Error in dataset ''' file_name ''': the ''' name ''' sheet observation dates do not cover all the ''Shares'' sheet observation dates.']);
    end

    t = numel(dates_num);
    n = numel(firm_names);
    tab = array2table(NaN(t,n),'VariableNames',tab_partial.Properties.VariableNames);

    for date = dates_from
        members_from = ismember(dates_from,date);
        members_to = ismember(dates_to,date);
        tab{members_to,:} = repmat(tab_partial{members_from,:},sum(members_to),1);
    end

end

function [tab,crises_dummy] = parse_table_crises_dates(file,file_name,index,name,date_format,dates_num)

    data_types = {'datetime' 'char'};
    tab = read_table(file,file_name,index,name,date_format,data_types);

    if (~isequal(tab.Properties.VariableNames,{'Date' 'Name'}))
        error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains invalid (wrong name) or misplaced (wrong order) columns.']);
    end

    dates_num_min = min(dates_num);
    dates_num_max = max(dates_num);

    dates_num_current = datenum(tab.Date); %#ok<DATNM>

    if (any(dates_num_current < dates_num_min) || any(dates_num_current > dates_num_max))
        error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains crisis events outside the scope of the dataset (' datestr(dates_num_min,date_format) ' - ' datestr(dates_num_max,date_format) ').']); %#ok<DATST>
    end

    n = height(tab);
    crises_dummy = zeros(numel(dates_num),1);

    for i = 1:n
        [check,index] = ismember(datenum(tab.Date(i)),dates_num); %#ok<DATNM>

        if (~check)
            error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains crisis events outside the scope of the dataset.']);
        end

        crises_dummy(index) = 1;
    end

    if (all(crises_dummy == 1))
        error(['Error in dataset ''' file_name ''': all the observation are considered to be part of a distress period.']);
    end

end

function [tab,crisis_dummies,crises_dummy] = parse_table_crises_ranges(file,file_name,index,name,date_format,dates_num)

    data_types = {'char' 'datetime' 'datetime'};
    tab = read_table(file,file_name,index,name,date_format,data_types);

    if (~isequal(tab.Properties.VariableNames,{'Name' 'StartDate' 'EndDate'}))
        error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains invalid (wrong name) or misplaced (wrong order) columns.']);
    end

    if (any(cellfun(@length,tab.Name) > 30))
        error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains one or more crises with a name longer than 25 characters.']);
    end

    dates_num_min = min(dates_num);
    dates_num_max = max(dates_num);

    dates_num_start = datenum(tab.StartDate); %#ok<DATNM>
    dates_num_end = datenum(tab.EndDate); %#ok<DATNM>

    if (any(dates_num_start > dates_num_max) || any(dates_num_end < dates_num_min))
        error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains crises outside the scope of the dataset (' datestr(dates_num_min,date_format) ' - ' datestr(dates_num_max,date_format) ').']); %#ok<DATST>
    end

    if (any(dates_num_start > dates_num_end))
        error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains one or more crises with start date greater than end date.']);
    end

    n = height(tab);
    crisis_dummies = zeros(numel(dates_num),n);

    for i = 1:n
        seq = datenum(tab.StartDate(i):tab.EndDate(i)).'; %#ok<DATNM>
        crisis_dummies(ismember(dates_num,seq),i) = 1;
    end

    crises_dummy = double(sum(crisis_dummies,2) > 0);

    if (all(crises_dummy == 1))
        error(['Error in dataset ''' file_name ''': all the observation are considered to be part of a distress period.']);
    end

end

function [tab,group_counts] = parse_table_groups(file,file_name,index,name,firm_names)

    data_types = {'char' 'char' 'double'};
    tab = read_table(file,file_name,index,name,[],data_types);

    if (~isequal(tab.Properties.VariableNames,{'Name' 'ShortName' 'Count'}))
        error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains invalid (wrong name) or misplaced (wrong order) columns.']);
    end

    if (any(~isfinite(tab{:,end})))
        error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains invalid values.']);
    end

    group_counts = tab.Count;

    if (any(group_counts <= 0) || any(round(group_counts) ~= group_counts))
        error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains one or more groups with an invalid number of firms.']);
    end

    if (sum(group_counts) ~= numel(firm_names))
        error(['Error in dataset ''' file_name ''': in the ''' name ''' sheet, the number of firms must be equal to the one defined in the ''Shares'' sheet.']);
    end

    tab.Name = strtrim(tab.Name);
    tab.ShortName = strtrim(tab.ShortName);

    if (any(cellfun(@isempty,regexpi(tab.ShortName,'^[A-Z][A-Z0-9]{0,4}$'))))
        error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains groups with an invalid short name (containing invalid characters, not starting with a letter and/or greater than 5 characters).']);
    end

end

function tab = parse_table_standard(file,file_name,index,name,date_format,dates_num,firm_names,check_negatives)

    if (isempty(firm_names))
        data_types = [];
    else
        data_types = [{'datetime'} repmat({'double'},1,numel(firm_names))];
    end

    tab = read_table(file,file_name,index,name,date_format,data_types);

    if (~strcmp(tab.Properties.VariableNames(1),'Date'))
        error(['Error in dataset ''' file_name ''': the first column of the ''' name ''' sheet must be called ''Date'' and must contain the observation dates.']);
    end

    if (any(any(~isfinite(tab{:,2:end}))))
        error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains invalid values.']);
    end

    if (check_negatives)
        if (~isempty(firm_names) && strcmp(firm_names{1},'RF'))
            if (any(any(tab{:,3:end} < 0)))
                error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains negative values.']);
            end
        else
            if (any(any(tab{:,2:end} < 0)))
                error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains negative values.']);
            end
        end
    end

    t_current = height(tab);
    dates_num_current = datenum(tab.Date); %#ok<DATNM>

    if (t_current ~= numel(unique(dates_num_current)))
        error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains duplicate observation dates.']);
    end

    if (any(dates_num_current ~= sort(dates_num_current)))
        error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains unsorted observation dates.']);
    end

    if (~isempty(dates_num))
        if ((t_current ~= numel(dates_num)) || any(dates_num_current ~= dates_num))
            error(['Error in dataset ''' file_name ''': the observation dates between the ''Shares'' sheet and the ''' name ''' sheet are mismatching.']);
        end

        tab.Date = [];
    end

    if (~isempty(firm_names) && ~isequal(tab.Properties.VariableNames,firm_names))
        error(['Error in dataset ''' file_name ''': the firm names between the ''Shares'' sheet and the ''' name ''' sheet are mismatching.']);
    end

end

function tab = read_table(file,file_name,index,name,date_format,data_types)

    date_format_dt = date_format;
    date_format_dt = strrep(date_format_dt,'m','M');
    date_format_dt = strrep(date_format_dt,'QQ','QQQ');

    data_types_count = numel(data_types);

    if (verLessThan('MATLAB','9.1'))
        try
            if (ispc())
                try
                    tab = readtable(file,'Sheet',index,'Basic',true);
                catch
                    tab = readtable(file,'Sheet',index);
                end
            else
                tab = readtable(file,'Sheet',index);
            end
        catch e
            error(['Error in dataset ''' file_name ''': the ''' name ''' sheet could not be read.' new_line() e.message]);
        end

        output_vars = varfun(@class,tab,'OutputFormat','cell');

        if (data_types_count == 0)
            data_types_count = width(tab);

            if (data_types_count > 0)
                data_types = [{'datetime'} repmat({'double'},1,data_types_count - 1)];
            end
        end

        for i = 1:data_types_count
            tab = ensure_field_consistency(file_name,name,tab,i,output_vars{i},data_types{i},date_format_dt);
        end
    else
        if (ispc())
            options = detectImportOptions(file,'Sheet',index,'NumHeaderLines',0);
        else
            options = detectImportOptions(file,'Sheet',name,'NumHeaderLines',0);
        end

        if (data_types_count == 0)
            data_types_count = numel(options.VariableNames);

            if (data_types_count > 0)
                data_types = [{'datetime'} repmat({'double'},1,data_types_count - 1)];
            end
        end

        options = setvartype(options,options.VariableNames(1:data_types_count),data_types);

        dt_indices = strcmp(data_types,'datetime');

        if (any(dt_indices))
            dt_variables = options.VariableNames(dt_indices);
            options = setvaropts(options,dt_variables,'InputFormat',date_format_dt);
        end

        try
            if (ispc())
                try
                    tab = readtable(file,options,'Basic',true);
                catch
                    tab = readtable(file,options);
                end
            else
                tab = readtable(file,options);
            end
        catch e
            error(['Error in dataset ''' file_name ''': the ''' name ''' sheet could not be read.' new_line() e.message]);
        end
    end

    h = height(tab);
    w = width(tab);

    if (h == 0)
        error(['Error in dataset ''' file_name ''': the ''' name ''' sheet must contain at least 1 row.']);
    end

    imr = all(ismissing(tab),2);
    f = find(diff([false; imr; false] == 0));

    if (~isempty(f))
        indices = f(1:2:end-1);
        counts = f(2:2:end) - indices;

        index_last = indices(end);
        count_last = counts(end);

        if ((index_last + count_last - 1) == h)
            tab(index_last:end,:) = [];
        end
    end

    isc = all(ismissing(tab),1).';
    f = find(diff([false; isc; false] == 0));

    if (~isempty(f))
        indices = f(1:2:end-1);
        counts = f(2:2:end) - indices;

        index_last = indices(end);
        count_last = counts(end);

        if ((index_last + count_last - 1) == w)
            tab(:,index_last:end) = [];
        end
    end

    if ((data_types_count > 1) && (width(tab) ~= data_types_count))
        error(['Error in dataset ''' file_name ''': the ''' name ''' sheet must contain exactly ' num2str(data_types_count) ' columns.']);
    end

    if (~all(cellfun(@isempty,regexp(tab.Properties.VariableNames,'^Var\d+$','once'))))
        error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains unnamed columns.']);
    end

    if (any(any(ismissing(tab))))
        error(['Error in dataset ''' file_name ''': the ''' name ''' sheet contains missing values.']);
    end

end

%% COMPUTATIONS

function [defaults,insolvencies] = detect_distress(distress_threshold,shares,volumes,capitalizations,cds,equity)

    [t,n] = size(shares);
    limit = round(t * distress_threshold,0);

    data = {shares volumes capitalizations cds};
    k = sum(~cellfun(@isempty,data,'UniformOutput',true));

    data = [shares volumes capitalizations cds];
    defaults = NaN(1,n);

    for i = 1:n
        data_i = data(:,i:n:(n * k));
        defaults_i = NaN(1,k);

        for j = 1:k
            data_ij = data_i(:,j);

            f = find(diff([1; data_ij; 1] == 0));

            if (~isempty(f))
                indices = f(1:2:end-1);
                counts = f(2:2:end) - indices;

                index_last = indices(end);
                count_last = counts(end);

                if (((index_last + count_last - 1) == t) && (count_last >= limit))
                    defaults_i(j) = index_last;
                end
            end
        end

        defaults(i) = min(defaults_i,[],'omitnan');
    end

    insolvencies = NaN(1,n);

    if (~isempty(equity))
        for i = 1:n
            eq = equity(:,i);

            f = find(diff([false; eq < 0; false] ~= 0));

            if (~isempty(f))
                indices = f(1:2:end-1);
                counts = f(2:2:end) - indices;

                index_last = indices(end);
                count_last = counts(end);

                if (((index_last + count_last - 1) == numel(eq)) && (count_last >= limit))
                    insolvencies(i) = index_last;
                end
            end
        end
    end

end

function data = remove_first_observation(data)

    if (~isempty(data))
        data = data(2:end,:);
    end

end


% Example of using CaptureFigVid
% Cheers, Dr. Alan Jennings, Research assistant professor,
% Department of Aeronautics and Astronautics, Air Force Institute of Technology

%% Set up 3D plot to record
figure(171);clf;
surf(peaks,'EdgeColor','none','FaceColor','interp','FaceLighting','phong')
daspect([1,1,.3]);axis tight;

%% Set up recording parameters (optional), and record
OptionZ.FrameRate=15;OptionZ.Duration=5.5;OptionZ.Periodic=true;
CaptureFigVid([-20,10;-110,10;-190,80;-290,10;-380,10], 'WellMadeVid',OptionZ)


function candoit = canGetPartialGradient(problem)
% Checks whether the partial gradient can be computed for a given problem.
%
% function candoit = canGetPartialGradient(problem)
%
% Returns true if the partial gradient of the cost function can be computed
% given the problem description, false otherwise.
%
% See also: getPartialGradient canGetPartialEuclideanGradient

% This file is part of Manopt: www.manopt.org.
% Original author: Nicolas Boumal, June 28, 2016.
% Contributors:
% Change log:

    candoit = (isfield(problem, 'partialgrad') && isfield(problem, 'ncostterms')) || ...
              canGetPartialEuclideanGradient(problem);

    if isfield(problem, 'partialgrad') && ~isfield(problem, 'ncostterms')
        warning('manopt:partialgrad', ...
               ['If problem.partialgrad is specified, indicate the number n\n' ...
                'of terms in the cost function with problem.ncostterms = n.']);
    end

end


%% Copyright (C) 2014-2019, 2022 Colin B. Macdonald
%%
%% 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
%% @deffn  Command syms
%% @deffnx Command syms @var{x}
%% @deffnx Command syms {@var{x} @var{y} @dots{}}
%% @deffnx Command syms @var{f(x)}
%% @deffnx Command syms {@var{x} @var{asm}}
%% @deffnx Command syms {@var{x} @var{asm} @var{asm2} @dots{}}
%% Create symbolic variables and symbolic functions.
%%
%% This is a convenience function.  For example:
%% @example
%% syms x y z
%% @end example
%% instead of:
%% @example
%% @group
%% x = sym('x');
%% y = sym('y');
%% z = sym('z');
%% @end group
%% @end example
%%
%% The last arguments can provide one or more assumptions (type or
%% restriction) on the variable (@pxref{sym}).
%% @example
%% @group
%% syms x y z positive
%% syms n positive even
%% @end group
%% @end example
%%
%% Symfuns represent abstract or concrete functions.  Abstract
%% symfuns can be created with @code{syms}:
%% @example
%% syms f(x)
%% @end example
%% Here @code{x} is created in the callers workspace,
%% as a @strong{side effect}.
%%
%% Called without arguments, @code{syms} displays a list of
%% all symbolic functions defined in the current workspace.
%% @example
%% @group
%% syms x y z
%% syms
%%   @print{} Symbolic variables in current scope:
%%   @print{}   x
%%   @print{}   y
%%   @print{}   z
%% @end group
%% @end example
%%
%% Using @code{syms} to create a new symbol with different assumptions
%% does not automatically replace instances of the old symbol in the
%% caller's workspace.  For example suppose we make an absolute value
%% expression:
%% @example
%% @group
%% syms x
%% f = abs (x)
%%   @result{} f = (sym) │x│
%% @end group
%% @end example
%% If we make a new positive @code{x}, @code{f} still contains the old
%% symbol:
%% @example
%% @group
%% syms x positive
%% simplify (f)
%%   @result{} (sym) │x│
%% @end group
%% @end example
%% Note: this behaviour is slightly different from Matlab's Symbolic
%% Math Toolbox, which does change existing symbols.
%% If that behaviour is desired, @pxref{@@sym/assume} which modifies
%% existing expressions in the caller's workspace:
%% @example
%% @group
%% syms x
%% assume x positive
%% f
%%   @result{} f = (sym) x
%% @end group
%% @end example
%%
%% Caution: On Matlab, you may not want to use @code{syms} within
%% functions.
%%   In particular, if you shadow a function name, you may get
%%   hard-to-track-down bugs.  For example, instead of writing
%%   @code{syms alpha} use @code{alpha = sym('alpha')} within functions.
%%   [https://www.mathworks.com/matlabcentral/newsreader/view_thread/237730]
%%
%% @seealso{sym, assume}
%% @end deffn

%% Author: Colin B. Macdonald
%% Keywords: symbolic, symbols, CAS

function syms(varargin)

  %% No inputs
  %output names of symbolic vars
  if (nargin == 0)
    S = evalin('caller', 'whos');
    disp('Symbolic variables in current scope:')
    for i=1:numel(S)
      %S(i)
      if strcmp(S(i).class, 'sym')
        disp(['  ' S(i).name])
      elseif strcmp(S(i).class, 'symfun')
        % FIXME improve display of symfun
        disp(['  ' S(i).name ' (symfun)'])
      end
    end
    return
  end

  %% Find assumptions
  valid_asm = assumptions('possible');
  last = -1;
  for n=1:nargin
    assert(ischar(varargin{n}), 'syms: expected string inputs')
    if (ismember(varargin{n}, valid_asm))
      if (last < 0)
        last = n - 1;
      end
    elseif (strcmp(varargin{n}, 'clear'))
      error ('OctSymPy:oldsyntax', ...
             'Old "syms x clear" not supported; use "assume x clear" or "assume(x, ''clear'')"')
    elseif (last > 0)
      error('syms: cannot have symbols after assumptions')
    end
  end

  if (last < 0)
    asm = {};
    exprs = varargin;
  elseif (last == 0)
    error('syms: cannot have only assumptions w/o symbols')
  else
    asm = varargin((last+1):end);
    exprs = varargin(1:last);
  end

  % loop over each input
  for i = 1:length(exprs)
    expr = exprs{i};

    % look for parenthesis: check if we're making a symfun
    if (isempty (strfind (expr, '(') ))  % no
      assert(isvarname(expr)); % help prevent malicious strings
      assignin('caller', expr, sym(expr, asm{:}))

    else  % yes, this is a symfun
      assert(isempty(asm), 'mixing symfuns and assumptions not supported')
      % regex matches: abc(x,y), f(var), f(x, y, z), f(r2d2), f( x, y )
      % should not match: Rational(2, 3), f(2br02b)
      assert(~isempty(regexp(expr, '^\w+\(\s*[A-z]\w*(,\s*[A-z]\w*)*\s*\)$')), ...
             'invalid symfun expression')

      T = regexp (expr, '^(\w+)\((.*)\)$', 'tokens');
      assert (length (T) == 1 && length (T{1}) == 2)
      name = T{1}{1};
      varnames = strtrim (strsplit (T{1}{2}, ','));

      vars = {};
      for j = 1:length (varnames)
        % is var in the workspace?
        try
          v = evalin ('caller', varnames{j});
          create = false;
        catch
          create = true;
        end
        % if var was defined, is it a symbol with the right name?
        if (~ create)
          if (~ isa (v, 'sym'))
            create = true;
          elseif (~ strcmp (v.flat, varnames{j}))
            create = true;
          end
        end
        if (create)
          v = sym (varnames{j});
          assignin ('caller', varnames{j}, v);
        end
        vars{j} = v;
      end

      cmd = { 'f, vars = _ins'
              'return Function(f)(*vars)' };
      s = pycall_sympy__ (cmd, name, vars);

      sf = symfun(s, vars);
      assignin('caller', name, sf);
    end
  end
end

%!test
%! %% assumptions
%! syms x real
%! x2 = sym('x', 'real');
%! assert (isequal (x, x2))

%!test
%! % assumptions and clearing them on a symbol
%! syms x real
%! assert (~isempty (assumptions (x)))
%! syms x
%! assert (isempty (assumptions (x)))

%!test
%! % Note SMT would clear syms in existing expressions
%! syms x real
%! f = {x {2*x} cos(x/2)};
%! assert (~isempty (assumptions (f)))
%! syms x
%! % but we do not: this would have to toggle for pure SMT compat
%! assert (~isempty (assumptions (f)))
%! % assert (isempty (assumptions (f)))

%!error <not supported>
%! syms x clear

%!error <symbols after assumptions>
%! syms x positive y

%!error <symbols after assumptions>
%! % this sometimes catches typos or errors in assumption names
%! % (if you need careful checking, use sym not syms)
%! syms x positive evne

%!error <cannot have only assumptions>
%! syms positive integer

%!test
%! % does not create a variable called positive
%! syms x positive integer
%! assert (logical(exist('x', 'var')))
%! assert (~logical(exist('positive', 'var')))

%!test
%! % Issue #885
%! syms S(x) I(x) O(x)

%!test
%! % Issue #290
%! syms FF(x)
%! syms ff(x)
%! syms Eq(x)

%!test
%! % Issue #290
%! syms beta(x)

%!test
%! syms x real
%! syms f(x)
%! assert (~ isempty (assumptions (x)))

%!test
%! syms x real
%! f(x) = symfun(sym('f(x)'), x);
%! assert (~ isempty (assumptions (x)))
%! assert (~ isempty (assumptions (argnames (f))))


function x = p18_root ( i )

%*****************************************************************************80
%
%% P18_ROOT returns a root for problem 18.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    04 October 2011
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer I, the index of the requested root.
%
%    Output, real X, the value of the root.
%
  if ( i == 1 )
    x = 1.0;
  else
    fprintf ( 1, '\n' );
    fprintf ( 1, 'P18_ROOT - Fatal error!\n' );
    fprintf ( 1, '  Illegal root index = %d\n', i );
    error ( 'P18_ROOT - Fatal error!' );
  end

  return
end


%% (Internal) Reshape a matrix into a row vector
% Return x as a row vector. This function is useful when a function returns a
% column vector or matrix and you want to immediately reshape it in a functional
% way. Suppose f(a,b,c) returns a column vector, matlab will not let you write
% f(a,b,c)(:)' - you would have to first store the result. With this function you
% can write rowvec(f(a,b,c)) and be assured that the result is a row vector.
%
%    x = colvec(x)
%
% Arguments:
%
%      + x: data
%
% Output:
%
%      + x: columnized data
%
% Example:
%
% See also colvec
%
% This file is from pmtk3.googlecode.com
% Copyright 2008-2015
%
function x = rowvec(x)
    x = x(:)';
end


%% SOBOL_TEST01 tests EXOR.
%
fprintf ( 1, '\n' );
fprintf ( 1, 'SOBOL_TEST01\n' );
fprintf ( 1, '  EXOR returns the exclusive OR of two integers.\n' );

fprintf ( 1, '\n' );
fprintf ( 1, '     I     J     EXOR(I,J)\n' );
fprintf ( 1, '\n' );

seed = get_seed ( 0 );

for ( test = 1 : 10 )

  [ i, seed ] = i_random ( 0, 100, seed );
  [ j, seed ] = i_random ( 0, 100, seed );
  k = exor ( i, j );

  fprintf ( 1, '%6d %6d %6d\n', i, j, k );

end


function i4mat_write ( output_filename, m, n, table )

%*****************************************************************************80
%
%% I4MAT_WRITE writes an I4MAT file.
%
%  Discussion:
%
%    An I4MAT is an array of I4's.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    08 February 2010
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string OUTPUT_FILENAME, the output filename.
%
%    Input, integer M, the spatial dimension.
%
%    Input, integer N, the number of points.
%
%    Input, integer TABLE(M,N), the points.
%
%    Input, logical HEADER, is TRUE if the header is to be included.
%

%
%  Open the file.
%
  output_unit = fopen ( output_filename, 'wt' );

  if ( output_unit < 0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'I4MAT_WRITE - Error!\n' );
    fprintf ( 1, '  Could not open the output file.\n' );
    error ( 'I4MAT_WRITE - Error!' );
  end
%
%  Write the data.
%
  for j = 1 : n
    for i = 1 : m
      fprintf ( output_unit, '  %12d', round ( table(i,j) ) );
    end
    fprintf ( output_unit, '\n' );
  end
%
%  Close the file.
%
  fclose ( output_unit );

  return
end


function [SF,SFI,SFJ] = remove_duplicate_simplices(F)
  % REMOVE_DUPLICATE_SIMPLICES Remove duplicate simplices (regardless of order)
  %
  % Inputs:
  %   F  #F by ss list of simplex indices
  % Outputs:
  %   SF  #SF by ss list of unique simplices (maintaining original order of
  %     first simplex found)
  %   SFI  #SF list so that SF = F(SFI,:)
  %   SFJ  #SFI list so that F = SF(SFJ,:)
  [~,SFI,SFJ] = unique(sort(F,2),'rows','stable');
  SF = F(SFI,:);
end


function Y = uminus(Y)
%UMINUS (overloaded)

if isa(Y,'blkvar')
    Y = sdpvar(Y);
end

switch Y.typeflag
    case {0,1,2,3,4}
        Y.basis = -Y.basis;
    case 5
        error('Cone object cannot be negated');
    otherwise
end
% Reset info about conic terms
Y.conicinfo = [0 0];
Y.extra.opname='';


%GETFEATDOM Get feature domain of a dataset
%
%	FEATDOM = GETFEATDOM(A,K)
%
% Returns the cell array with the feature domains of the dataset A.


function sT = som_table_modify(sT,action,arg1,arg2,arg3)

%SOM_TABLE_MODIFY Modify table: add or remove columns or rows.
%
% sTable = som_table_modify(sTable,action,arg1,[arg2],[arg3])
%
%  Input and output arguments ([]'s are optional):
%   sTable      (struct) table struct
%   action      (string) action id (see below).
%   arg1        (varies) Depending on action, 1 to 3 arguments
%   [arg2]      (varies) are needed. See below.
%   [arg3]      (varies)
%
%   sTable      (struct) the modified table struct
%
%  Actions and their arguments:
%   'addcol'    Add one or several new columns.
%               arg1 (cell array) new values
%                    (char)       new values (a single column can be given)
%                    (matrix)     new values
%               arg2 (cell array) new headers
%               arg3 (scalar)     at which position the new columns
%                                 should be inserted (at the end by default)
%   'addrow'    Add one or several new rows.
%               arg1 (cell array) new values
%                    (char)       new values (a single row can be given)
%                    (matrix)     new values
%               arg2 (scalar)     at which position the new rows
%                                 should be inserted (at the end by default)
%   'removecol' Remove one or several columns.
%               arg1 (vector)     indeces of columns to be removed
%   'removerow' Remove one or several rows.
%               arg1 (vector)     indeces of rows to be removed
%
% See also  SOM_TABLE_STRUCT, SOM_TABLE_PRINT.

% Contributed to SOM Toolbox 2.0, January 4th, 2002 by Juha Vesanto
% Copyright (c) by Juha Vesanto
% http://www.cis.hut.fi/projects/somtoolbox/

% Version 2.0beta juuso 040102

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

[nrT,ncT] = size(sT.values);

switch action,
 case 'addcol',
  values = arg1;
  if ischar(values), values = cellstr(values); end
  if isnumeric(values), values = num2cell(values); end
  spans  = ones([size(values) 2]);
  [nr,nc] = size(values);
  if nargin<4, header = cell(1,nc); header(:) = {''}; else header = arg2; end
  if ischar(header), header = cellstr(header); end
  if nargin<5, where = ncT+1; else  where  = arg3; end
  if nrT ~= nr,
    error('Mismatch between sizes of given table and additional columns')
  else
    sT.headers = [sT.headers(:,1:where-1), header, sT.headers(:,where:end)];
    sT.values  = [sT.values(:,1:where-1), values, sT.values(:,where:end)];
    sT.span    = [sT.span(:,1:where-1,:), spans, sT.span(:,where:end,:)];
  end
 case 'addrow',
  values  = arg1;
  if ischar(values), values = cellstr(values); end
  if isnumeric(values), values = num2cell(values); end
  [nr,nc] = size(values);
  spans   = ones([size(values) 2]);
  if nargin<4, where = nrT+1; else where  = arg2; end
  if ncT ~= nc,
    error('Mismatch between sizes of given table and additional rows')
  else
    sT.values = [sT.values(1:where-1,:); values; sT.values(where:end,:)];
    sT.span   = [sT.span(1:where-1,:,:); spans; sT.span(where:end,:,:)];
  end
 case 'removecol',
  where      = setdiff(1:ncT,arg1);
  sT.values  = sT.values(:,where);
  sT.headers = sT.headers(:,where);
  sT.span    = sT.span(:,where,:);
 case 'removerow',
  where      = setdiff(1:nrT,arg1);
  sT.values  = sT.values(where,:);
  sT.headers = sT.headers(where,:);
  sT.span    = sT.span(where,:,:);
end

return;

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


function y = in2cm(x)
%IN2CM converts inches to centimeters
%
%   Usage: y = in2cm(x)
%
%   Input parameters:
%       x     - input / inches
%
%   Output parameters:
%       y     - output / cm
%
%   See also: in2px, cm2px, cm2in, px2in, px2cm

%*****************************************************************************
% The MIT License (MIT)                                                      *
%                                                                            *
% Copyright (c) 2010-2019 SFS Toolbox Developers                             *
%                                                                            *
% Permission is hereby granted,  free of charge,  to any person  obtaining a *
% copy of this software and associated documentation files (the "Software"), *
% to deal in the Software without  restriction, including without limitation *
% the rights  to use, copy, modify, merge,  publish, distribute, sublicense, *
% and/or  sell copies of  the Software,  and to permit  persons to whom  the *
% Software is furnished to do so, subject to the following conditions:       *
%                                                                            *
% The above copyright notice and this permission notice shall be included in *
% all copies or substantial portions of the Software.                        *
%                                                                            *
% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
% IMPLIED, INCLUDING BUT  NOT LIMITED TO THE  WARRANTIES OF MERCHANTABILITY, *
% FITNESS  FOR A PARTICULAR  PURPOSE AND  NONINFRINGEMENT. IN NO EVENT SHALL *
% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
% LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT, TORT  OR OTHERWISE, ARISING *
% FROM,  OUT OF  OR IN  CONNECTION  WITH THE  SOFTWARE OR  THE USE  OR OTHER *
% DEALINGS IN THE SOFTWARE.                                                  *
%                                                                            *
% The SFS Toolbox  allows to simulate and  investigate sound field synthesis *
% methods like wave field synthesis or higher order ambisonics.              *
%                                                                            *
% https://sfs.readthedocs.io                            sfstoolbox@gmail.com *
%*****************************************************************************

%% ===== Checking of input parameters ====================================
nargmin = 1;
nargmax = 1;
narginchk(nargmin,nargmax);

%% ===== Computation =====================================================
y = x * 2.54;


function [lc,dflag,dattype]=loadcell(fname,delim,exclusions,options);
%function [lc,dflag,numdata]=loadcell(fname,delim,exclusions);
%
%  loadcell loads a cell array with character delimited
%  data, which can have variable length lines and content.
%  Numeric values are converted from string to double
%  unless options is a string containing 'string'.
%
%  loadcell is for use with small datasets. It is not optimised
%  for large datasets.
%
%  fname is the filename to be loaded
%
%  delim is/are the relevant delimiter(s). If char(10) is included
%  newlines are simply treated as delimiters and a 1-d array is created.
%
%  exclusions  are the set of characters to be treated as paired
%    braces: line ends or delimiters within braces are ignored.
%    braces are single characters and any brace can pair with
%    any other brace: no type pair checking is done.
%
%  options can be omitted or can contain 'string' if no numeric
%    conversion is required, 'single' if multiple adjacent seperators
%    should not be treated as one, 'free' if all linefeeds should be stripped
%    first and 'empty2num' if empty fields are to be treated as numeric
%    zeros rather than an empty character set. Combine options using
%    concatenation.
%
%  lc is a cell array containing the loaded data.
%
%  dflag is a set of flags denoting the (i,j) values where data was entered
%  dflag(i,j)=1 implies lc(i,j) was loaded from the data, and not just set
%  to empty, say, by default.
%
%  numdata is an array  numdata(i,j)=NaN implies
%    lc(i,j) is a string, otherwise it stores the number at i,j.
%    This will occur regardless of whether the 'string' option is set.
%
%  lc will return -1 if the file is not found or could not be
%  opened.
%
%  Hint: numdata+(1/dflag-1) provides a concise descriptor for the numeric data
%  Inf=not loaded
%  NaN=was string or empty set.
%  otherwise numeric
%
%  EXAMPLE
%
%[a,b]=loadcell('resultsfile',[',' char(9)],'"','single-string');
%   will load file 'resultsfile' into variable a, treating any of tab or
%   comma as delimiters. Delimiters or carriage returns lying
%   between two double inverted commas will be ignored. Two adjacent delimiters
%   will count twice, and all data will be kept as a string.
%
%   Note: in space-separated data 'single' would generally be omitted,
%   wheras in comma-separated data it would be included.
%
%   Note the exclusion characters will remain in the final data, and any data
%   contained within or containing exclusion characters will not be
%   converted to numerics.
%
%   (c) Amos Storkey 2002
%    v b160702

% MATLAB is incapable of loading variable length lines or variable type values
% with a whole file command under the standard library sets. This mfile
% fills that gap.
if (nargin<4)
    options=' ';
end;
dflag = [];
%Open file
fid=fopen(fname,'rt');
%Cannot open: return -1
if (fid<0)
  lc=-1;
else
  fullfile=fread(fid,'uchar=>char')';
  %Strip LF if free is set
  if ~isempty(findstr(options,'free'))
      fullfile=strrep(fullfile,char(10),'');
  end;
  %Find all delimiters
  delimpos=[];
  for s=1:length(delim)
    delimpos=[delimpos find(fullfile==delim(s))];
  end
  %Find all eol
  endpos=find(fullfile==char(10));
  endpos=setdiff(endpos,delimpos);
  %find all exclusions
  xclpos=[];
  for s=1:length(exclusions);
    xclpos=[xclpos find(fullfile==exclusions(s))];
  end
  sort(xclpos);
  xclpos=[xclpos(1:2:end-1);xclpos(2:2:end)];
  %Combine eol and delimiters
  jointpos=union(delimpos,endpos);
  t=1;
  %Remove delim/eol within exclusion pairs
  removedelim=[];
  for s=1:length(jointpos)
    if any((jointpos(s)>xclpos(1,:)) & (jointpos(s)<xclpos(2,:)))
      removedelim(t)=jointpos(s);
      t=t+1;
    end;

  end
  %and add start point
  jointpos=[0 setdiff(jointpos,removedelim)];
  i=1;
  j=1;
  posind=1;
  multflag=isempty(findstr(options,'single'));
  stringflag=~isempty(findstr(options,'string'));
  emptnum=~isempty(findstr(options,'empty2num'));
  %Run through
  while (posind<(length(jointpos)))
    %Get current field
    tempstr=fullfile(jointpos(posind)+1:jointpos(posind+1)-1);
    %If empty only continue if adjacent delim count.
    if ~(isempty(tempstr) & multflag);
      %This ij is set
      dflag(i,j)=1;
      %Convert to num
      tempno=str2double([tempstr]);
      %If emptystring convert to zero if emptnum set
      if (isempty(tempstr) & emptnum)
          tempno=0;
      end;
      %Set dattype to no (or NaN if not a num
      dattype(i,j)=tempno;
      %If NaN set lc to string else to num if stringflag not set
      if (isnan(tempno) |  stringflag)
        lc{i,j}=tempstr;
      else
        lc{i,j}=tempno;
      end;
      %Next j
      j=j+1;
    end;
    %If eol inc i and reset j
    if ismember(jointpos(posind+1),endpos)
        i=i+1;
        j=1;
    end;
    %Inc to next delim
    posind=posind+1;
  end;
end;
%Logicalise dflag
dflag=logical(dflag);


function region = p31_region ( )

%*****************************************************************************80
%
%% P31_REGION returns the name of the integration region for problem 31.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    02 June 2007
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Output, string REGION, the name of the integration region.
%
  region = 'box';

  return
end


% loadeep() - Load EEProbe continuous data (*.cnt).
%
% Usage:
%   >> [eep] = loadeep(file, varargin);
%
% Inputs:
%            filename    - name of ANT EEP file, including extension (*.cnt)
%
%   Optional inputs:
%           'time1'      - start time in seconds.
%           'sample1'    - start at sample1, default 0.
%                          (Overrules time1)
%           'time2'      - end time of data to be  read in seconds, default = whole file.
%           'sample2'    - end sample of data to be read, default = whole file.
%                          (Overrules time2)
% Outputs:
%   [eep]                - data structure holding continous EEG information and other
%                          relevant information.
%
% Author: Maarten-Jan Hoeve, ANT Software, Enschede, The Netherlands, 8 October 2003
%
% See also: eeglab(), pop_loadeep()
%

%123456789012345678901234567890123456789012345678901234567890123456789012

% Copyright (C) 2003 Maarten-Jan Hoeve, m.hoeve@ieee.org
%
% 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, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

% $Log: loadeep.m,v $
% Revision 1.3  2005/07/21 10:12:51  mvelde
% updated information
%
% Revision 1.2  2005/06/08 08:16:37  mvelde
% converted files to unix format
%
% Revision 1.1  2004/11/26 13:22:22  jwiskerke
% Added eeglab to cvs.
%
% Revision 1.4  2003/10/24 13:34:40  Maarten-Jan Hoeve
% Added GNU Licence and updated revision history
%
% Revision 1.1.2  2003/10/17 09:55:20  mvelde
% updated: consistent copyrights, arguments/data labels, fixed some typos
%
% Advanced Neuro Technology (ANT) BV, The Netherlands, www.ant-neuro.com / info@ant-neuro.com
%

function r=loadeep(file, varargin)

if nargin < 1
	help loadeep;
	return;
end;

% defaults
if ~any(file=='.'), file=[file '.cnt']; end
[datdir,name,ext]=fileparts(file);
disp(['Loading file ' file ' ...'])

% read short piece of data to get sampling rate, channels etc
eeg = read_eep_cnt(file,1,2);

if ~isempty(varargin)
	 r=struct(varargin{:});
else r = [];
end;

% add defaults
try, r.time1;       catch, r.time1=0; end
try, r.sample1;     catch, r.sample1=[]; end
try, r.time2;       catch, r.time2=[]; end
try, r.sample2;     catch, r.sample2=[]; end

r.filename=file;
r.totsamples=eeg.nsample;
r.nchannels=eeg.nchan;
r.rate=eeg.rate;

% Create struct for holding channel labels
for i=1:r.nchannels
    chanlocs(i).labels=char(eeg.label(i));
    chanlocs(i).theta=0;
    chanlocs(i).radius=0;
    chanlocs(i).X=0;
    chanlocs(i).Y=0;
    chanlocs(i).Z=0;
    chanlocs(i).sph_theta=0;
    chanlocs(i).sph_phi=0;
    chanlocs(i).sph_radius=0;
end
r.chanlocs=chanlocs;

if r.sample1
    s1=r.sample1;
else
    s1=((r.time1 * r.rate)+1);
    r.sample1=s1;
end

if r.sample2
    s2=r.sample2;
else
    if r.time2
        s2=((r.time2 * r.rate)+1);
        if (s2 > r.totsamples)|(s2 < s1); s2=r.totsamples; end
    else
        s2=r.totsamples;
    end
end

eeg=read_eep_cnt(file,s1,s2);
r.nsmpl=eeg.npnt;
r.time=eeg.time;
r.dat=eeg.data;


function [ x, indx, k, nstack, stacks, ncan ] = r8vec_backtrack ( n, ...
  x, indx, k, nstack, stacks, ncan )

%*****************************************************************************80
%
%% R8VEC_BACKTRACK supervises a backtrack search for an R8VEC.
%
%  Discussion:
%
%    The routine tries to construct a real vector one index at a time,
%    using possible candidates as supplied by the user.
%
%    At any time, the partially constructed vector may be discovered to be
%    unsatisfactory, but the routine records information about where the
%    last arbitrary choice was made, so that the search can be
%    carried out efficiently, rather than starting out all over again.
%
%    First, call the routine with INDX = 0 so it can initialize itself.
%
%    Now, on each return from the routine, if INDX is:
%      1, you've just been handed a complete candidate vector;
%         Admire it, analyze it, do what you like.
%      2, please determine suitable candidates for position X(K).
%         Return the number of candidates in NCAN(K), adding each
%         candidate to the end of STACKS, and increasing NSTACK.
%      3, you're done.  Stop calling the routine;
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    28 January 2011
%
%  Author:
%
%    Original FORTRAN77 version by Albert Nijenhuis, Herbert Wilf.
%    MATLAB version by John Burkardt.
%
%  Reference:
%
%    Albert Nijenhuis, Herbert Wilf.
%    Combinatorial Algorithms,
%    Academic Press, 1978, second edition,
%    ISBN 0-12-519260-6.
%
%  Parameters:
%
%    Input, integer N, the number of positions to be filled in the vector.
%
%    Input, real X(N), the partially filled in candidate vector.
%
%    Input, integer INDX, a communication flag.
%    * 0, to begin a backtracking search.
%    * 2, the requested candidates for position K have been added to
%      STACKS, and NCAN(K) was updated.
%
%    Input, integer K, the index in X that we are trying to fill.
%
%    Input, integer NSTACK, the current length of the stack.
%
%    Input, real STACKS(MAXSTACK), a list of all current candidates for
%    all positions 1 through K.
%
%    Input, integer NCAN(N), lists the current number of candidates for
%    all positions 1 through K.
%
%    Output, real X(N), the partially filled in candidate vector.
%
%    Output, integer INDX, a communication flag.
%    * 1, a complete output vector has been determined and returned in X(1:N);
%    * 2, candidates are needed for position X(K);
%    * 3, no more possible vectors exist.
%
%    Output, integer K, the index in X that we are trying to fill.
%
%    Output, integer NSTACK, the current length of the stack.
%
%    Output, real STACKS(MAXSTACK), a list of all current candidates for
%    all positions 1 through K.
%
%    Output, integer NCAN(N), lists the current number of candidates for
%    all positions 1 through K.
%

%
%  If this is the first call, request a candidate for position 1.
%
  if ( indx == 0 )
    k = 1;
    nstack = 0;
    indx = 2;
    return
  end
%
%  Examine the stack.
%
  while ( 1 )
%
%  If there are candidates for position K, take the first available
%  one off the stack, and increment K.
%
%  This may cause K to reach the desired value of N, in which case
%  we need to signal the user that a complete set of candidates
%  is being returned.
%
    if ( 0 < ncan(k) )

      x(k) = stacks(nstack);
      nstack = nstack - 1;
      ncan(k) = ncan(k) - 1;

      if ( k ~= n )
        k = k + 1;
        indx = 2;
      else
        indx = 1;
      end

      break
%
%  If there are no candidates for position K, then decrement K.
%  If K is still positive, repeat the examination of the stack.
%
    else

      k = k - 1;

      if ( k <= 0 )
        indx = 3;
        break
      end

    end

  end

  return
end


function field_values = get_field(db, field_name)
    NUM_OF_ENTRIES = length(db);
    field_values = cell(NUM_OF_ENTRIES, 1);

    for k = 1:NUM_OF_ENTRIES
        metadata = getmeta(db, k);
        field_values{k} = metadata.(field_name);
    end;

    [field_values, ~] = unique(field_values);
end


function mtype = mwrite (filename, A, Z, comments_filename)		    %#ok
%MWRITE write a matrix to a file in Matrix Market form.
%
%   Example:
%        mtype = mwrite (filename, A, Z, comments_filename)
%
% A can be sparse or full.
%
% If present and non-empty, A and Z must have the same dimension.  Z contains
% the explicit zero entries in the matrix (which MATLAB drops).  The entries
% of Z appear as explicit zeros in the output file.  Z is optional.  If it is
% an empty matrix it is ignored.  Z must be sparse or empty, if present.
% It is ignored if A is full.
%
% filename is the name of the output file.  comments_filename is the file
% whose contents are include after the Matrix Market header and before the
% first data line.  Ignored if an empty string or not present.
%
% See also mread.

% Copyright 2006-2007, Timothy A. Davis

error ('mwrite mexFunction not found') ;


function [node_ele_assignment] = revert_assignment(elements,sparse_flag)

% REVERT_ASSIGNMENT
%
% $Id$

N_nodes = max(max(elements));
N_elements = max(size(elements));
[sort_elements_vec, sort_ind] = sort(elements(:));
[dummy, m, dummy] = unique(sort_elements_vec,'last');
occurence = diff([0;m]);

node_ele_assignment = zeros(max(occurence),N_nodes);
sort_ind = mod(sort_ind,N_elements);
sort_ind(sort_ind == 0) = N_elements;
flip = diff([sort_elements_vec;sort_elements_vec(end)]);
col = 1;
row = 1;

for i=1:length(flip)
    node_ele_assignment(row,col) = sort_ind(i);
    row = row+1;
    if(flip(i))
        col = col+1;
        row = 1;
    end
end

s1 = whos('node_ele_assignment');
sp = sparse(node_ele_assignment);
s2 = whos('sp');
if(s2.bytes/s1.bytes < sparse_flag)
    node_ele_assignment = sp;
end

end


function [ nb, ni, np, ns, pr_bud, pr_bud_angular, r, seed ] = ...
  parameter_set ( nb, ni, np, ns, pr_bud, pr_bud_angular, r, seed )

%*****************************************************************************80
%
%% PARAMETER_SET allows the user to reset parameter values;
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    27 December 2012
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input/output, integer NB, the number of boundary points.
%
%    Input/output, integer NI, the number of interior points.
%
%    Input/output, integer NP, the total number of points.
%
%    Input/output, integer NS, the number of sample points.
%
%    Input/output, real PR_BUD, the probability that a boundary point will bud.
%
%    Input/output, real PR_BUD_ANGULAR, the probability that a boundary point bud
%    will be constrained to the boundary.
%
%    Input/output, real R, the radius of the circle.
%
%    Input/output, integer SEED, a seed for the random number generator.
%
  fprintf ( 1, '\n' );
  fprintf ( 1, '  Use the "who" command to see current variables\n' );
  fprintf ( 1, '  Reset any value using the usual MATLAB assignment statements.\n' );
  fprintf ( 1, '  To terminate assignments and resume the program, type "return".\n' );
  fprintf ( 1, '  To terminate the program, type "dbquit".\n' );

  keyboard

  return
end


function [ t_start, y_start ] = p34_start ( neqn )

%*****************************************************************************80
%
%% P34_START returns the starting point for problem p34.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    23 February 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer NEQN, the number of equations.
%
%    Output, real T_START, Y_START(NEQN,1), the initial data.
%
  y_start = zeros ( neqn, 1 );

  t_start = 0.0;
  y_start = [ 2.0; 2.0 ];

  return
end


function options = cplexoptimset (varargin)
%%
% Purpose
% Create or edit optimization options structure.
%
% Syntax
%    options = cplexoptimset('param1',value1,'param2',value2,...)
%    cplexoptimset
%    options = cplexoptimset
%    options = cplexoptimset('cplex')
%    options = cplexoptimset(oldopts,'param1',value1,...)
%    options = cplexoptimset(oldopts,newopts)
%
% Description
% options = cplexoptimset('param1',value1,'param2',value2,...) creates an
% optimization options structure called options, in which the specified
% options (param) have specified values. The specified options correspond
% to the options in the MATLAB(R) Optimization Toolbox.Any unspecified
% options are set to [] (options with value [] indicate that the default
% value for that option should be used when options is passed to the
% optimization function).
%
% cplexoptimset with no input or output arguments displays a complete list
% of options with their valid values.
%
% options = cplexoptimset (with no input arguments) creates an option
% structure options where all fields are set to [], which instructs
% CPLEX(R) to use all default parameter values.
%
% options = cplexoptimset ('cplex') creates a structure options, which
% contains all of the CPLEX parameters.  Use this method when you need to
% set parameters that do not correspond to the options in the MATLAB
% Optimization Toolbox.
%
% options = cplexoptimset(oldopts,'param1',value1,...) creates a copy of
% oldopts, modifying the specified options with the specified values.
%
% options = cplexoptimset(oldopts,newopts) combines an existing options
% structure, oldopts, with a new options structure, newopts. Any options in
% newopts with nonempty values overwrite the corresponding old options in
% oldopts.
%
% Options corresponding to the MATLAB Optimization Toolbox
% Diagnostics          'on' | {'off'}
% Display              'off' | 'iter' | 'final' | 'notify'
% MaxIter               refer to cplex.Param.simplex.limits.iterations
% Simplex               refer to cplex.Param.lpmethod
%                       refer to cplex.Param.qpmethod
%                       refer to cplex.Param.mip.strategy.startalgorithm
% BranchStrategy        refer to cplex.Param.mip.strategy.variableselect
% MaxNodes              refer to cplex.Param.mip.limits.nodes
% MaxTime               refer to cplex.Param.timelimit
% NodeDisplayInterval   refer to cplex.Param.mip.interval
% NodeSearchStrategy    refer to cplex.Param.mip.strategy.nodeselect
% TolFun                refer to cplex.Param.simplex.tolerances.optimality
% TolXInteger           refer to cplex.Param.mip.tolerances.integrality
% TolRLPFun             refer to cplex.Param.simplex.tolerances.optimality
%
% For other CPLEX options, refer to the CPLEX Parameters Reference
% Manual.
%
% Examples
% To turn on the optimizer output and set a node limit of 400:
% options = cplexoptimset('Diagnostics', 'on', 'MaxNodes', 400);
%
% To set a node limit of 400 and instruct CPLEX to use traditional branch
% and cut style search:
% opt = cplexoptimset('cplex');
% opt.mip.limits.nodes=400;
% opt.mip.strategy.search=1;
%
%  See also cplexoptimget
%

% ---------------------------------------------------------------------------
% File: cplexoptimset.m
% ---------------------------------------------------------------------------
% Licensed Materials - Property of IBM
% 5725-A06 5725-A29 5724-Y48 5724-Y49 5724-Y54 5724-Y55
% Copyright IBM Corporation 2008, 2010. All Rights Reserved.
%
% US Government Users Restricted Rights - Use, duplication or
% disclosure restricted by GSA ADP Schedule Contract with
% IBM Corp.
% ---------------------------------------------------------------------------


%% Create dataset object
% Our bonds are located in a number of XLS-files.  Through a simple
% FOR-loop, we can grab all of the bonds and place them into a simgle
% workspace variable.
data = dataset;
for yearIdx = 1980:2000
    fileName = ['bonds' num2str(yearIdx) '.xls'];
    data = [data; dataset('xlsfile', fileName)];
end
% This single command is all we need to work with the data set.  There are,
% however, three improvements that we can make by applying our knowledge of
% the nature of this dataset.  These changes will make operations on the
% array have a simpler syntax and operate faster, and they will make the
% memory footprint smaller as an added bonus.

% Variable size is 1370656 bytes.
clearvars -except data
%% Tuning the dataset
%% 1. Nominal arrays
% Three fields (the ticker symbol, the description, and the currency) are
% filled with often-repeated, categorical data.  These should be recast
% into nominal arrays, which are more efficient at storing the repeated
% data and are also much faster for indexing and searching purposes.

data.ticker      = nominal(data.ticker);
data.description = nominal(data.description);
data.currency    = nominal(data.currency);
% Variable size is now 859844 bytes: 63% of the original size.

%% 2. Ordinal arrays
% The "index quality" field gives the bond ratings as assigned by S&P.
% These are similar to nominal array in that there are often-repeated
% labels, but furthermore there is now a definite order in that some
% ratings are better than others.  This is a perfect use for ordinal
% arrays.  All we must do is supply the ratings scale from lowest to
% highest (or else it will default to alphabetical order):

ratingsScale = ...
    {'BBB-', 'BBB', 'BBB+', 'A-', 'A', 'A+', 'AA-', 'AA', 'AA+', 'AAA'};
data.indexQuality = ordinal(data.indexQuality, [], ratingsScale);
% Variable size is now 701570 bytes: 51% of the original size.

%% 3. Change dates into MATLAB date numbers
% The three date columns are, by default, imported as character strings.
% Using the DATENUM command we can convert them to MATLAB's numeric data
% format, which allows for relational indexing.  It also uses less memory.

data.valDate      = datenum(data.valDate);
data.maturityDate = datenum(data.maturityDate);
% Some small savvy is required to properly handle the blank dates in the
% data.call_date set:
data.callDate     = cellfun(@datenan, data.callDate);
% Variable size is now 206982 bytes: only 15% of the original size!

%% 4. Load another dataset with sector information
% If we have data in different locations we can read in the data into
% another dataset and tune the dataset.
sectorInfo = dataset('xlsfile', 'sectorInfo.xls');

sectorInfo.ticker = nominal(sectorInfo.ticker);
sectorInfo.description = nominal(sectorInfo.description);
sectorInfo.sector = nominal(sectorInfo.sector);

%% 5. Join datasets together
% Once the dataset is tuned we can use the join function to join the two
% datasets based on a similar column or key.  Here we join the datasets
% based on the ticker to include sector information that was not in the
% original data we brought in.
dataFull = join(data, sectorInfo, 'key', 'ticker', 'RightVars', 'sector');

%% Some simple examples of data manipulation and indexing using datasets
% Now that we have a finely-tuned dataset, we can peform a number of
% intuitive indexing tasks as well as some operations.
%% 1. Searching for all instruments of a given ticker name
% First, let us list all of the ticker labels in our array.  This
% information is stored in the data.ticker nominal array.  We can list all
% categories in a nominal array with the GETLABELS function...

getlabels(dataFull.ticker)
%%
% ...and we can focus on a specific ticker (say, Good Foods) with array
% indexing:

dataFull( dataFull.ticker == 'GOOD', {'description', 'par', 'marketValue'} )

%% 2. Searching for all instruments of certain grades
% Thanks to ordinal arrays, we can search for all high- and prime-grade
% instruments with a single command:

dataFull( dataFull.indexQuality >= 'AA-', {'ticker', 'par', 'indexQuality'})

%% 3. Searching for all instruments of certain valuation dates
% With MATLAB serial dates, we can use relational operators to find all
% instruments between certain dates:
subset = dataFull( dataFull.valDate >= datenum('Dec-01-1980') ...
    & dataFull.valDate <= datenum('Jan-01-1981'), {'description', 'coupon', 'par'} )

%%
% Incidentally, it is straightforward to export a dataset to a variety of
% delimited files.  Here, we export the subset created above to a
% tab-delimited file:
export( subset, 'XLSFile', 'Dec1980.xls')

%% 4. A simple example of taking an average on a 2-month block of data
% Let us suppose we wanted to find the simple average coupon and par value
% of the instruments with a valuation date between 12/01/80 and 01/01/81,
% (the same date range as above):
avgCouponAndPar = datasetfun( @mean, dataFull( dataFull.valDate >= datenum('Dec-01-1980') ...
    & dataFull.valDate <= datenum('Jan-01-1981'), {'coupon', 'par'}) )

%% 5. A way to easily look at group statisics
% If we wanted to see the mean of the coupon for each of the instruments,
% we can use the GRPSTATS function:
avgCoupon = grpstats(dataFull, 'ticker', {@mean, @std}, 'DataVars', 'coupon')

%% 6. Other visualizations
% We can create a heatmap function that allows us to quickly look at how
% our portfolio is distributed across the different bonds.
generate_heatmap(dataFull);


function [r, g, b] = imSplitChannels(rgb)
% Split the 3 channels of a 2D or 3D image.
%
%   [R, G, B] = imSplitChannels(RGB);
%
%   Example
%   img = imread('peppers.png');
%   [r, g, b] = imSplitChannels(img);
%   figure; imshow(r); title('red');
%   figure; imshow(g); title('green');
%
%   See also
%     imMergeChannels

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

if ndims(rgb) == 3
    % process planar image
    r = rgb(:,:,1);
    g = rgb(:,:,2);
    b = rgb(:,:,3);

elseif ndims(rgb) == 4
    % process 3D image
    r = squeeze(rgb(:,:,1,:));
    g = squeeze(rgb(:,:,2,:));
    b = squeeze(rgb(:,:,3,:));
else
    error('unprocessed image dimension');
end


%% Ke Ma, Christopher Bodden
% CS 766 - Project 2

%% simple callback for enlarging graphs/images
function enlargeImage(handles, falseColor)
im = get( gcbo,'cdata' );
imtool(im, [min(im(:)) max(im(:))] )

if exist('falseColor')
    colormap(jet(256));
end
end