 function hn=arrow3(p1,p2,s,w,h,ip,alpha,beta)
% ARROW3 (R13)
%   ARROW3(P1,P2) draws lines from P1 to P2 with directional arrowheads.
%   P1 and P2 are either nx2 or nx3 matrices.  Each row of P1 is an
%   initial point, and each row of P2 is a terminal point.
%
%   ARROW3(P1,P2,S,W,H,IP,ALPHA,BETA) can be used to specify properties
%   of the line, initial point marker, and arrowhead.  S is a character
%   string made with one element from any or all of the following 3
%   columns:
%
%     Color Switches      LineStyle            LineWidth
%     ------------------  -------------------  --------------------
%     k  blacK (default)  -  solid (default)   0.5 points (default)
%     y  Yellow           :  dotted            0   no lines
%     m  Magenta          -. dashdot           /   LineWidthOrder
%     c  Cyan             -- dashed
%     r  Red              *  LineStyleOrder            _______ __
%     g  Green                                       ^        |
%     b  Blue                                       / \       |
%     w  White                        Arrowhead    /   \   Height
%     a  Asparagus                                /     \     |
%     d  Dark gray                               /       \    |
%     e  Evergreen                              /___   ___\ __|__
%     f  Firebrick                             |    | |    |
%     h  Hot pink                              |-- Width --|
%     i  Indigo                                |    | |    |
%     j  Jade                                       | |
%     l  Light gray                                 | |
%     n  Nutbrown                                   | |
%     p  Pear                                       | |
%     q  kumQuat                      Line       -->| |<--LineWidth
%     s  Sky blue                                   | |
%     t  Tawny                                      | |
%     u  bUrgundy                                   | |
%     v  Violet                                     | |
%     z  aZure                                      | |
%     x  random                       Initial      /   \
%     o  colorOrder                   Point    -->(     )<--IP
%     |  magnitude                    Marker       \_ _/
%
%     -------------------------------------------------------------
%                          Color Equivalencies
%     -------------------------------------------------------------
%     ColorOrder     Arrow3         |     Simulink       Arrow3
%     ----------     ----------     |     ----------     ----------
%     Color1         Blue           |     LightBlue      aZure
%     Color2         Evergreen      |     DarkGreen      Asparagus
%     Color3         Red            |     Orange         kumQuat
%     Color4         Sky blue       |     Gray           Light gray
%     Color5         Violet         |
%     Color6         Pear           |
%     Color7         Dark gray      |
%     -------------------------------------------------------------
%
%   The components of S may be specified in any order.  Invalid
%   characters in S will be ignored and replaced by default settings.
%
%   Prefixing the color code with '_' produces a darker shade, e.g.
%   '_t' is dark tawny; prefixing the color code with '^' produces a
%   lighter shade, e.g. '^q' is light kumquat.  The relative brightness
%   of light and dark color shades is controlled by the scalar parameter
%   BETA.  Color code prefixes do not affect black (k), white (w), or
%   the special color switches (xo|).
%
%   ColorOrder may be achieved in two fashions:  The user may either
%   set the ColorOrder property (using RGB triples) or define the
%   global variable ColorOrder (using a string of valid color codes).
%   If the color switch is specified with 'o', and the global variable
%   ColorOrder is a string of color codes (color switches less 'xo|',
%   optionally prefixed with '_' or '^'), then the ColorOrder property
%   will be set to the sequence of colors indicated by the ColorOrder
%   variable.  The color sequence 'bersvpd' matches the default
%   ColorOrder property.  If the color switch is specified with 'o', and
%   the global variable ColorOrder is empty or invalid, then the current
%   ColorOrder property will be used.  Note that the ColorOrder variable
%   takes precedence over the ColorOrder property.
%
%   The magnitude color switch is used to visualize vector magnitudes
%   in conjunction with a colorbar.  If the color switch is specified
%   with '|', colors are linearly interpolated from the current ColorMap
%   according to the length of the associated line.  This option sets
%   CLim to [MinM,MaxM], where MinM and MaxM are the minimum and maximum
%   magnitudes, respectively.
%
%   The current LineStyleOrder property will be used if LineStyle is
%   specified with '*'.  MATLAB cycles through the line styles defined
%   by the LineStyleOrder property only after using all colors defined
%   by the ColorOrder property.  If however, the global variable
%   LineWidthOrder is defined, and LineWidth is specified with '/',
%   then each line will be drawn with sequential color, linestyle, and
%   linewidth.
%
%   W (default = 1) is a vector of arrowhead widths; use W = 0 for no
%   arrowheads.  H (default = 3W) is a vector of arrowhead heights.  If
%   vector IP is neither empty nor negative, initial point markers will
%   be plotted with diameter IP; for default diameter W, use IP = 0.
%   The units of W, H and IP are 1/72 of the PlotBox diagonal.
%
%   ALPHA (default = 1) is a vector of FaceAlpha values ranging between
%   0 (clear) and 1 (opaque).  FaceAlpha is a surface (arrowhead and
%   initial point marker) property and does not affect lines.  FaceAlpha
%   is not supported for 2D rendering.
%
%   BETA (default = 0.4) is a scalar that controls the relative
%   brightness of light and dark color shades, ranging between 0 (no
%   contrast) and 1 (maximum contrast).
%
%   ARROW3(P1,P2,S,W,H,'cone',...) will plot cones with bases centered
%   on P1 in the direction given by P2.  In this instance, P2 is
%   interpreted as a direction vector instead of a terminal point.
%   Neither initial point markers nor lines are plotted with the 'cone'
%   option.
%
%   HN = ARROW3(P1,P2,...) returns a vector of handles to line and
%   surface objects created by ARROW3.
%
%   ARROW3 COLORS will plot a table of named colors with default
%   brightness.  ARROW3('colors',BETA) will plot a table of named
%   colors with brightness BETA.
%
%   ARROW3 attempts to preserve the appearance of existing axes.  In
%   particular, ARROW3 will not change XYZLim, View, or CameraViewAngle.
%   ARROW3 does not, however, support stretch-to-fill scaling.  AXIS
%   NORMAL will restore the current axis box to full size and remove any
%   restrictions on the scaling of units, but will likely result in
%   distorted arrowheads and initial point markers.  See
%   (arrow3_messes_up_my_plots.html).
%
%   If a particular aspect ratio or variable limit is required, use
%   DASPECT, PBASPECT, AXIS, or XYZLIM commands before calling ARROW3.
%   Changing limits or aspect ratios after calling ARROW3 may alter the
%   appearance of arrowheads and initial point markers.  ARROW3 sets
%   XYZCLimMode to manual for all plots, sets DataAspectRatioMode to
%   manual for linear plots, and sets PlotBoxAspectRatioMode to manual
%   for log plots and 3D plots.  CameraViewAngleMode is also set to
%   manual for 3D plots.
%
%   ARROW3 UPDATE will restore the appearance of arrowheads and
%   initial point markers that have become corrupted by changes to
%   limits or aspect ratios.  ARROW3('update',SF) will redraw initial
%   point markers and arrowheads with scale factor SF.  If SF has one
%   element, SF scales W, H and IP.  If SF has two elements, SF(1)
%   scales W and IP, and SF(2) scales H.  If SF has three elements,
%   SF(1) scales W, SF(2) scales H, and SF(3) scales IP.  All sizes are
%   relative to the current PlotBox diagonal.
%
%   ARROW3 UPDATE COLORS will update the magnitude coloring of
%   arrowheads, initial point markers, and lines to conform to the
%   current ColorMap.
%
%   HN = ARROW3('update',...) returns a vector of handles to updated
%   objects.
%
%   EXAMPLES:
%
%     % 2D vectors
%     arrow3([0 0],[1 3])
%     arrow3([0 0],[1 2],'-.e')
%     arrow3([0 0],[10 10],'--x2',1)
%     arrow3(zeros(10,2),50*rand(10,2),'x',1,3)
%     arrow3(zeros(10,2),[10*rand(10,1),500*rand(10,1)],'u')
%     arrow3(10*rand(10,2),50*rand(10,2),'x',1,[],1)
%
%     % 3D vectors
%     arrow3([0 0 0],[1 1 1])
%     arrow3(zeros(20,3),50*rand(20,3),'--x1.5',2)
%     arrow3(zeros(100,3),50*rand(100,3),'x',1,3)
%     arrow3(zeros(10,3),[10*rand(10,1),500*rand(10,1),50*rand(10,1)],'a')
%     arrow3(10*rand(10,3),50*rand(10,3),'x',[],[],0)
%
%     % 3D animation
%     t=(0:pi/40:8*pi)'; u=cos(t); v=sin(t);
%     plot3(20*t,u,v)
%     axis([0,600,-1.5,1.5,-1.5,1.5])
%     grid on, view(35,25)
%     hold on
%     pbaspect([1.8,1.4,1])
%     arrow3(zeros(3),diag([500,1.5,1.5]),'l',0.7,[],0)
%     p=[20*t,u,v]; inc=4:1:length(t);
%     p2=p(inc,:); p1=p(inc-1,:);
%     hn=arrow3(p1(1,:),p2(1,:),'0_b',0.7);
%     for i=2:1:length(p1)
%       delete(hn)
%       hn=arrow3(p1(i,:),p2(i,:),'0_b',0.7);
%       pause(0.01)
%     end
%     hold off
%
%     % Cone plot
%     t=(pi/8:pi/8:2*pi)'; p1=[cos(t) sin(t) t]; p2=repmat([0 0 1],16,1);
%     arrow3(p1,p2,'x',2,4,'cone'), hold on
%     plot3(p1(:,1),p1(:,2),p1(:,3)), hold off
%     pause % change cone size
%     arrow3('update',[1,2])
%
%     % Just for fun
%     arrow3(zeros(100,3),50*rand(100,3),'x',8,4,[],0.95)
%     light('position',[-10 -10 -10],'style','local')
%     light('position',[60,60,60]), lighting gouraud
%
%     % ColorOrder variable, color code prefixes, and Beta
%     global ColorOrder, ColorOrder='^ui^e_hq^v';
%     theta=[0:pi/22:pi/2]';
%     arrow3(zeros(12,2),[cos(theta),sin(theta)],'1.5o',1.5,[],[],[],0.5)
%
%     % ColorOrder property, LineStyleOrder, and LineWidthOrder
%     global ColorOrder, ColorOrder=[];
%     set(gca,'ColorOrder',[1,0,0;0,0,1;0.25,0.75,0.25;0,0,0])
%     set(gca,'LineStyleOrder',{'-','--','-.',':'})
%     global LineWidthOrder, LineWidthOrder=[1,2,4,8];
%     w=[1,2,3,4]; h=[4,6,4,2];
%     arrow3(zeros(4,2),[10*rand(4,1),500*rand(4,1)],'o*/',w,h,0)
%
%     % Magnitude coloring
%     colormap spring
%     arrow3(20*randn(20,3),50*randn(20,3),'|',[],[],0)
%     set(gca,'color',0.7*[1,1,1])
%     set(gcf,'color',0.5*[1,1,1]), grid on, colorbar
%     pause % change the ColorMap and update colors
%     colormap hot
%     arrow3('update','colors')
%
%     % LogLog plot
%     set(gca,'xscale','log','yscale','log');
%     axis([1e2,1e8,1e-2,1e-1]); hold on
%     p1=repmat([1e3,2e-2],15,1);
%     q1=[1e7,1e6,1e5,1e4,1e3,1e7,1e7,1e7,1e7,1e7,1e7,1e6,1e5,1e4,1e3];
%     q2=1e-2*[9,9,9,9,9,7,5,4,3,2,1,1,1,1,1]; p2=[q1',q2'];
%     global ColorOrder, ColorOrder=[];
%     set(gca,'ColorOrder',rand(15,3))
%     arrow3(p1,p2,'o'), grid on, hold off
%
%     % SemiLogX plot
%     set(gca,'xscale','log','yscale','linear');
%     axis([1e2,1e8,1e-2,1e-1]); hold on
%     p1=repmat([1e3,0.05],15,1);
%     q1=[1e7,1e6,1e5,1e4,1e3,1e7,1e7,1e7,1e7,1e7,1e7,1e6,1e5,1e4,1e3];
%     q2=1e-2*[9,9,9,9,9,7,5,4,3,2,1,1,1,1,1]; p2=[q1',q2'];
%     arrow3(p1,p2,'x'), grid on, hold off
%
%     % SemiLogY plot
%     set(gca,'xscale','linear','yscale','log');
%     axis([2,8,1e-2,1e-1]); hold on
%     p1=repmat([3,2e-2],17,1);
%     q1=[7,6,5,4,3,7,7,7,7,7,7,7,7,6,5,4,3];
%     q2=1e-2*[9,9,9,9,9,8,7,6,5,4,3,2,1,1,1,1,1]; p2=[q1',q2'];
%     set(gca,'LineStyleOrder',{'-','--','-.',':'})
%     arrow3(p1,p2,'*',1,[],0), grid on, hold off
%
%     % Color tables
%     arrow3('colors')           % default color table
%     arrow3('colors',0.3)       % low contrast color table
%     arrow3('colors',0.5)       % high contrast color table
%
%     % Update initial point markers and arrowheads
%     % relative to the current PlotBox diagonal
%     arrow3('update')           % redraw same size
%     arrow3('update',2)         % redraw double size
%     arrow3('update',0.5)       % redraw half size
%     arrow3('update',[0.5,2,1]) % redraw W half size,
%                                %        H double size, and
%                                %        IP same size
%
%     See also (arrow3_examples.html), (arrow3_messes_up_my_plots.html).

%   Copyright(c)2002-2020 Version 5.18.1
%     Tom Davis (tdavis@earthsurvey.us)
%     Jeff Chang

%   Revision History:
%
%     08/08/20 - Address ColorOrder, LineStyleOrder property changes
%                in R2019b. (TD)
%     06/19/20 - Remove deprecated zbuffer renderer. (TD)
%     11/02/19 - Address ColorOrder property changes in R2019b. (TD)
%     08/28/18 - Use explicit EdgeColor. (TD)
%     01/15/13 - Use AppData instead of UserData. (TD)
%     07/27/11 - Added animation example. (TD)
%     05/13/09 - Corrected spelling errors (TD)
%     03/16/08 - Updated contact information (TD)
%     10/23/07 - Corrected zero magnitude exclusion (TD)
%     09/08/07 - Added cone plot option; removed adaptive grid
%                spacing; corrected scale factor; removed "nearly"
%                tight limits (TD)
%     07/24/07 - Ignore zero-magnitude input (TD)
%     07/08/07 - Modified named colors to match named Simulink
%                colors; added light and dark shades for basic
%                colors (ymcrgb) (TD)
%     07/01/07 - Modified named colors to match default ColorOrder
%                colors (TD)
%     06/24/07 - Error checking for empty P1, P2 (TD)
%     06/17/07 - Trim colors,W,H,IP,ALPHA to LENGTH(P1) (TD)
%     05/27/07 - Magnitude coloring and documentation revision (TD)
%     03/10/07 - Improved code metrics (TD)
%     02/21/07 - Preserve existing axis appearance;
%                use relative sizes for W, H, and IP;
%                removed version checking; minor bug fixes (TD)
%     01/09/04 - Replaced calls to LINSPACE, INTERP1, and
%                COLORMAP (TD)
%     12/17/03 - Semilog examples, CAXIS support, magnitude
%                coloring, and color updating; use CData instead
%                of FaceColor; minor bug fixes (TD)
%     07/17/03 - Changed 2D rendering from OpenGL to ZBuffer;
%                defined HN for COLORS and UPDATE options (TD)
%     02/27/03 - Replaced calls to RANDPERM, VIEW, REPMAT, SPHERE,
%                and CYLINDER; added ZBuffer for log plots, RESET
%                for CLA and CLF, and ABS for W and H (TD)
%     02/01/03 - Added UPDATE scale factor and MATLAB version
%                checking, replaced call to CROSS (TD)
%     12/26/02 - Added UserData and UPDATE option (TD)
%     11/16/02 - Added more named colors, color code prefix,
%                global ColorOrder, ALPHA , and BETA (TD)
%     10/12/02 - Added global LineWidthOrder,
%                vectorized W, H and IP (TD)
%     10/05/02 - Changed CLF to CLA for subplot support,
%                added ColorOrder and LineStyleOrder support (TD)
%     04/27/02 - Minor log plot revisions (TD)
%     03/26/02 - Added log plot support (TD)
%     03/24/02 - Adaptive grid spacing control to trade off
%                appearance vs. speed based on size of matrix (JC)
%     03/16/02 - Added "axis tight" for improved appearance (JC)
%     03/12/02 - Added initial point marker (TD)
%     03/03/02 - Added aspect ratio support (TD)
%     03/02/02 - Enhanced program's user friendliness (JC)
%                (lump Color, LineStyle, and LineWidth together)
%     03/01/02 - Replaced call to ROTATE (TD)
%     02/28/02 - Modified line plotting,
%                added linewidth and linestyle (TD)
%     02/27/02 - Minor enhancements on 3D appearance (JC)
%     02/26/02 - Minor enhancements for speed (TD&JC)
%     02/26/02 - Optimize PLOT3 and SURF for speed (TD)
%     02/25/02 - Return handler, error handling, color effect,
%                generalize for 2D/3D vectors (JC)
%     02/24/02 - Optimize PLOT3 and SURF for speed (TD)
%     02/23/02 - First release (JC&TD)

%-------------------------------------------------------------------------
% Error Checking
global LineWidthOrder ColorOrder
if nargin<8 || isempty(beta), beta=0.4; end
beta=abs(beta(1)); if nargout, hn=[]; end
if strcmpi(p1,'colors')                            % plot color table
  if nargin>1, beta=abs(p2(1)); end
  LocalColorTable(1,beta); return
end
fig=gcf; ax=gca;
if strcmpi(p1,'update'), ad=getappdata(ax,'Arrow3');    % update
  LocalLogCheck(ax);
  if size(ad,2)<13, error('Invalid AppData'), end
  setappdata(ax,'Arrow3',[]); sf=[1,1,1]; flag=0;
  if nargin>1
    if strcmpi(p2,'colors'), flag=1;               % update colors
    elseif ~isempty(p2)                            % update surfaces
      sf=p2(1)*sf; n=length(p2(:));
      if n>1, sf(2)=p2(2); if n>2, sf(3)=p2(3); end, end
    end
  end
  H=LocalUpdate(fig,ax,ad,sf,flag); if nargout, hn=H; end, return
end
InputError=['Invalid input, type HELP ',upper(mfilename),...
  ' for usage examples'];
if nargin<2, error(InputError), end
[r1,c1]=size(p1); [r2,c2]=size(p2);
if c1<2 || c1>3 || r1*r2==0, error(InputError), end
if r1~=r2, error('P1 and P2 must have same number of rows'), end
if c1~=c2, error('P1 and P2 must have same number of columns'), end
p=sum(abs(p2-p1),2)~=0; cone=0;
if nargin>5 && ~isempty(ip) && strcmpi(ip,'cone')  % cone plot
  cone=1; p=sum(p2,2)~=0;
  if ~any(p), error('P2 cannot equal 0'), end
  set(ax,'tag','Arrow3ConePlot');
elseif ~any(p), error('P1 cannot equal P2')
end
if ~all(p)
  warning('Arrow3:ZeroMagnitude','Zero magnitude ignored')
  p1=p1(p,:); p2=p2(p,:); [r1,c1]=size(p1);
end
n=r1; Zeros=zeros(n,1);
if c1==2, p1=[p1,Zeros]; p2=[p2,Zeros];
elseif ~any([p1(:,3);p2(:,3)]), c1=2; end
L=get(ax,'LineStyleOrder'); C=get(ax,'ColorOrder');
ST=get(ax,'DefaultSurfaceTag'); LT=get(ax,'DefaultLineTag');
EC=get(ax,'DefaultSurfaceEdgeColor');
if strcmp(get(ax,'nextplot'),'add') && strcmp(get(fig,'nextplot'),'add')
  Xr=get(ax,'xlim'); Yr=get(ax,'ylim'); Zr=get(ax,'zlim');
  [xs,ys,xys]=LocalLogCheck(ax); restore=1;
  if xys, mode='auto';
    if any([p1(:,3);p2(:,3)]), error('3D log plot not supported'), end
    if (xs && ~all([p1(:,1);p2(:,1)]>0)) || ...
       (ys && ~all([p1(:,2);p2(:,2)]>0))
       error('Nonpositive log data not supported')
    end
  else, mode='manual';
    if strcmp(get(ax,'WarpToFill'),'on')
      warning('Arrow3:WarpToFill',['Stretch-to-fill scaling not ',...
        'supported;\nuse DASPECT or PBASPECT before calling ARROW3.']);
    end
  end
  set(ax,'XLimMode',mode,'YLimMode',mode,'ZLimMode',mode,...
    'CLimMode','manual');
else, restore=0; cla reset; xys=0; set(fig,'nextplot','add');
  set(ax,'LineStyleOrder',L);
  if c1==2, azel=[0,90]; else, azel=[-37.5,30]; end
  setappdata(ax,'Arrow3',[]);
  set(ax,'nextplot','add','View',azel);
end

%-------------------------------------------------------------------------
% Style Control
[vc,cn]=LocalColorTable(0); prefix='';
if nargin<3, [c,ls,lw]=LocalValidateCLSW;% default color, linestyle/width
else
  [c,ls,lw]=LocalValidateCLSW(s);
  if length(c)>1, if sum('_^'==c(1)), prefix=c(1); end, c=c(2); end
  if c=='x'                              % random named color (less white)
    [~,i]=sort(rand(1,23)); c=cn(i,:);
  elseif c=='o'                                    % ColorOrder
    if ~isempty(ColorOrder)
      [c,failed]=LocalColorMap(lower(ColorOrder),vc,cn,beta);
      if failed, ColorOrderWarning=['Invalid ColorOrder ',...
        'variable, current ColorOrder property will be used'];
        warning('Arrow3:ColorOrder',ColorOrderWarning)
      else, C=c;
      end
    end, c=C;
  elseif c=='|', map=get(fig,'colormap');          % magnitude coloring
    M=(p1-p2); M=sqrt(sum(M.*M,2)); minM=min(M); maxM=max(M);
    if maxM-minM<1, minM=0; end
    set(ax,'clim',[minM,maxM]); c=LocalInterp(minM,maxM,map,M);
  elseif ~sum(vc==c), c='k'; ColorWarning=['Invalid color switch, ',...
    'default color (black) will be used'];
    warning('Arrow3:Color',ColorWarning)
  end
end
if length(c)==1                                    % single color
  c=LocalColorMap([prefix,c],vc,cn,beta);
end
set(ax,'ColorOrder',c); c=LocalRepmat(c,[ceil(n/size(c,1)),1]);
if ls~='*'                                         % LineStyleOrder
  set(ax,'LineStyleOrder',ls);
end
ls=get(ax,'LineStyleOrder');
ls=LocalRepmat(cellstr(ls),[ceil(n/size(ls,1)),1]);
if lw=='/'                                         % LineWidthOrder
  if ~isempty(LineWidthOrder)
    lw=LineWidthOrder;
  else, lw=0.5; LineWidthOrderWarning=['Undefined LineWidthOrder, ',...
    'default width (0.5) will be used'];
    warning('Arrow3:LineWidthOrder',LineWidthOrderWarning)
  end
end
lw=LocalRepmat(lw,[ceil(n/length(lw)),1]);
if nargin<4 || isempty(w), w=1; end                % width
w=LocalRepmat(abs(w(:)),[ceil(n/length(w)),1]);
if nargin<5 || isempty(h), h=3*w; end              % height
h=LocalRepmat(abs(h(:)),[ceil(n/length(h)),1]);
if nargin>5 && ~isempty(ip) && ~cone               % ip
  ip=LocalRepmat(ip(:),[ceil(n/length(ip)),1]);
  i=find(ip==0); ip(i)=w(i);
else, ip=-ones(n,1);
end
if nargin<7 || isempty(alpha), alpha=1; end
a=LocalRepmat(alpha(:),[ceil(n/length(alpha)),1]); % FaceAlpha

%-------------------------------------------------------------------------
% Log Plot
if xys
  units=get(ax,'units'); set(ax,'units','points');
  pos=get(ax,'position'); set(ax,'units',units);
  if strcmp(get(ax,'PlotBoxAspectRatioMode'),'auto')
    set(ax,'PlotBoxAspectRatio',[pos(3),pos(4),1]);
  end
  par=get(ax,'PlotBoxAspectRatio');
  set(ax,'DataAspectRatio',[par(2),par(1),par(3)]);
  % map coordinates onto unit square
  q=[p1;p2]; xr=Xr; yr=Yr;
  if xs, xr=log10(xr); q(:,1)=log10(q(:,1)); end
  if ys, yr=log10(yr); q(:,2)=log10(q(:,2)); end
  q=q-LocalRepmat([xr(1),yr(1),0],[2*n,1]);
  dx=xr(2)-xr(1); dy=yr(2)-yr(1);
  q=q*diag([1/dx,1/dy,1]);
  q1=q(1:n,:); q2=q(n+1:end,:);
else, xs=0; ys=0; dx=0; dy=0; xr=0; yr=0;
end

%-------------------------------------------------------------------------
% Line
set(ax,'DefaultLineTag','arrow3');
if ~cone
  H1=Zeros;
  for i=1:n
    if lw(i)
      H1(i)=plot3([p1(i,1),p2(i,1)],[p1(i,2),p2(i,2)],...
        [p1(i,3),p2(i,3)],ls{i},'Color',c(i,:),'LineWidth',lw(i));
    end
  end
else                                               % cone plot
  P=zeros(3*n,3); i=1:n;
  P(3*i-2,:)=p1(i,:); P(3*i-1,:)=p1(i,:); P(3*i,1)=NaN;
  H1=plot3(P(:,1),P(:,2),P(:,3));
end

%-------------------------------------------------------------------------
% Scale
if ~restore, axis tight, end
ar=get(ax,'DataAspectRatio'); ar=sqrt(3)*ar/norm(ar);
set(ax,'DataAspectRatioMode','manual');
if xys, sf=1;
else, xr=get(ax,'xlim'); yr=get(ax,'ylim'); zr=get(ax,'zlim');
  sf=norm(diff([xr;yr;zr],1,2)./ar')/72;
end

%-------------------------------------------------------------------------
% AppData
c=c(1:n,:); w=w(1:n); h=h(1:n); ip=ip(1:n); a=a(1:n);
setappdata(ax,'Arrow3',[getappdata(ax,'Arrow3');p1,p2,c,w,h,ip,a]);

%-------------------------------------------------------------------------
% Arrowhead
whip=sf*[w,h,ip];
if xys, whip=whip*sqrt(2)/72; p1=q1; p2=q2; end
w=whip(:,1); h=whip(:,2); ip=whip(:,3);
if cone                                            % cone plot
  delete(H1), H1=[];
  p2=p2./LocalRepmat(sqrt(sum(p2.*p2,2)),[1,3]);
  p2=p1+p2.*LocalRepmat(ar,[n,1]).*LocalRepmat(h,[1,3]);
end
W=(p1-p2)./LocalRepmat(ar,[n,1]);
W=W./LocalRepmat(sqrt(sum(W.*W,2)),[1,3]);         % new z direction
U=[-W(:,2),W(:,1),Zeros];
N=sqrt(sum(U.*U,2)); i=find(N<eps); j=length(i);
U(i,:)=LocalRepmat([1,0,0],[j,1]); N(i)=ones(j,1);
U=U./LocalRepmat(N,[1,3]);                         % new x direction
V=[W(:,2).*U(:,3)-W(:,3).*U(:,2),...               % new y direction
  W(:,3).*U(:,1)-W(:,1).*U(:,3),...
  W(:,1).*U(:,2)-W(:,2).*U(:,1)];
m=20;                               % surface grid spacing
set(ax,'DefaultSurfaceTag','arrow3','DefaultSurfaceEdgeColor','none');
r=[0;1]; theta=(0:m)/m*2*pi; Ones=ones(1,m+1);
x=r*cos(theta); y=r*sin(theta); z=r*Ones;
G=surface(x/2,y/2,z); dar=diag(ar);
set(G,'EdgeColor','none');
X=get(G,'XData'); Y=get(G,'YData'); Z=get(G,'ZData');
H2=Zeros; [j,k]=size(X);
for i=1:n                           % translate, rotate, and scale
  H2(i)=copyobj(G,ax);
  xyz=[w(i)*X(:),w(i)*Y(:),h(i)*Z(:)]*[U(i,:);V(i,:);W(i,:)]*dar;
  x=reshape(xyz(:,1),j,k)+p2(i,1);
  y=reshape(xyz(:,2),j,k)+p2(i,2);
  z=reshape(xyz(:,3),j,k)+p2(i,3);
  LocalSetSurface(xys,xs,ys,dx,dy,xr,yr,...
    x,y,z,a(i),c(i,:),H2(i),2,m+1);
end
delete(G);

%-------------------------------------------------------------------------
% Initial Point Marker
if any(ip>0)
  theta=(-m:2:m)/m*pi; phi=(-m:2:m)'/m*pi/2; cosphi=cos(phi);
  x=cosphi*cos(theta); y=cosphi*sin(theta); z=sin(phi)*Ones;
  G=surface(x*ar(1)/2,y*ar(2)/2,z*ar(3)/2);
  set(G,'EdgeColor','none');
  X=get(G,'XData'); Y=get(G,'YData'); Z=get(G,'ZData');
  H3=zeros(n,1);
  for i=1:n                                        % translate
    if ip(i)>0
      H3(i)=copyobj(G,ax);
      x=p1(i,1)+X*ip(i); y=p1(i,2)+Y*ip(i); z=p1(i,3)+Z*ip(i);
      LocalSetSurface(xys,xs,ys,dx,dy,xr,yr,...
        x,y,z,a(i),c(i,:),H3(i),m+1,m+1);
    end
  end, delete(G);
else, H3=[];
end

%-------------------------------------------------------------------------
% Finish
if restore, xr=Xr; yr=Yr; zr=Zr;
  if xys, set(ax,'DataAspectRatioMode','auto'); end
else
  axis tight
  xr=get(ax,'xlim'); yr=get(ax,'ylim'); zr=get(ax,'zlim');
  set(ax,'nextplot','replace');
end
azel=get(ax,'view');
if abs(azel(2))==90, c1=2; else, c1=3; end
set(fig,'Renderer','OpenGL');
set(ax,'LineStyleOrder',L,'ColorOrder',C,'DefaultLineTag',LT,...
  'DefaultSurfaceTag',ST,'DefaultSurfaceEdgeColor',EC,...
  'xlim',xr,'ylim',yr,'zlim',zr,'clim',get(ax,'CLim'));
if c1==3, set(ax,'CameraViewAngle',get(ax,'CameraViewAngle'),...
   'PlotBoxAspectRatio',get(ax,'PlotBoxAspectRatio'));
end
if nargout, hn=[H1(:);H2(:);H3(:)]; end

%-------------------------------------------------------------------------
% Local Functions
%-------------------------------------------------------------------------
% Update
function H=LocalUpdate(fig,ax,ad,sf,flag)
global ColorOrder
p1=ad(:,1:3); p2=ad(:,4:6); c=ad(:,7:9); a=ad(:,13);
w=sf(1)*ad(:,10); h=sf(2)*ad(:,11); ip=sf(3)*ad(:,12);
H=get(ax,'children'); tag=get(H,'tag'); type=get(H,'type');
delete(H(strcmp(tag,'arrow3') & strcmp(type,'surface')));
set(fig,'nextplot','add'); set(ax,'nextplot','add'); H1=[];
if flag, map=get(fig,'colormap');                  % update colors
  M=(p1-p2); M=sqrt(sum(M.*M,2)); minM=min(M); maxM=max(M);
  H1=H(strcmp(tag,'arrow3') & strcmp(type,'line'));
  MagnitudeWarning=['Cannot perform magnitude coloring on lines ',...
    'that\nwere drawn with a single color, linestyle, and linewidth'];
  if length(H1)>1
    for i=1:length(H1)                             % update line colors
      x=get(H1(i),'xdata'); y=get(H1(i),'ydata'); z=get(H1(i),'zdata');
      if length(x)>2                               % multiple lines
        warning('Arrow3:Magnitude',MagnitudeWarning), continue
      end
      m=sqrt((x(1)-x(2))^2+(y(1)-y(2))^2+(z(1)-z(2))^2);
      c=LocalInterp(minM,maxM,map,m); set(H1(i),'color',c);
    end
  elseif length(H1)==1
    warning('Arrow3:Magnitude',MagnitudeWarning)
  end
  c=LocalInterp(minM,maxM,map,M);
end
set(ax,'ColorOrder',c);                            % update surfaces
ColorOrder=[];
if strcmp(get(ax,'tag'),'Arrow3ConePlot')
      H=arrow3(p1,p2,'o' ,w,h,'cone',a);           % update cones
else, H=arrow3(p1,p2,'o0',w,h,    ip,a);
end,  H=[H1(:);H(:)];
set(ax,'nextplot','replace');

%-------------------------------------------------------------------------
% SetSurface
function LocalSetSurface(xys,xs,ys,dx,dy,xr,yr,x,y,z,a,c,H,n,m)
if xys
  x=x*dx+xr(1); y=y*dy+yr(1);
  if xs, x=10.^x; end
  if ys, y=10.^y; end
end
cd=zeros(n,m,3); cd(:,:,1)=c(1); cd(:,:,2)=c(2); cd(:,:,3)=c(3);
set(H,'XData',x,'YData',y,'ZData',z,'CData',cd,'FaceAlpha',a);

%-------------------------------------------------------------------------
% ColorTable
function [vc,cn]=LocalColorTable(n,beta)
vc='kymcrgbadefhijlnpqstuvzw';                     % valid color codes
%                k               y               m               c
cn=[0.00,0.00,0.00; 1.00,1.00,0.00; 1.00,0.00,1.00; 0.00,1.00,1.00;
%                r               g               b               a
    1.00,0.00,0.00; 0.00,1.00,0.00; 0.00,0.00,1.00; 0.42,0.59,0.24;
%                d               e               f               h
    0.25,0.25,0.25; 0.00,0.50,0.00; 0.70,0.13,0.13; 1.00,0.41,0.71;
%                i               j               l               n
    0.29,0.00,0.51; 0.00,0.66,0.42; 0.50,0.50,0.50; 0.50,0.20,0.00;
%                p               q               s               t
    0.75,0.75,0.00; 1.00,0.50,0.00; 0.00,0.75,0.75; 0.80,0.34,0.00;
%                u               v               z               w
    0.50,0.00,0.13; 0.75,0.00,0.75; 0.38,0.74,0.99; 1.00,1.00,1.00];

% Named Simulink Colors (zaql)
% LightBlue = 0.38  0.74  0.99 = aZure
% DarkGreen = 0.42  0.59  0.24 = Asparagus
% Orange    = 1.00  0.50  0.00 = kumQuat
% Gray      = 0.50  0.50  0.50 = Light gray
%
% Default ColorOrder Property Colors (bersvpd)
% Color1    = 0.00  0.00  1.00 = Blue
% Color2    = 0.00  0.50  0.00 = Evergreen
% Color3    = 1.00  0.00  0.00 = Red
% Color4    = 0.00  0.75  0.75 = Sky blue
% Color5    = 0.75  0.00  0.75 = Violet
% Color6    = 0.75  0.75  0.00 = Pear
% Color7    = 0.25  0.25  0.25 = Dark gray

if n, clf reset                                    % plot color table
  name={'blacK','Yellow','Magenta','Cyan',...
    'Red','Green','Blue','Asparagus',...
    'Dark gray','Evergreen','Firebrick','Hot pink',...
    'Indigo','Jade','Light gray','Nutbrown',...
    'Pear','kumQuat','Sky blue','Tawny',...
    'bUrgundy','Violet','aZure','White'};
  c=['yptn';'gjae';'czsb';'hmvi';'qrfu';'wldk'];
  set(gcf,'DefaultAxesXTick',[],'DefaultAxesYTick',[],...
    'DefaultAxesXTickLabel',[],'DefaultAxesYTickLabel',[],...
    'DefaultAxesXLim',[0,0.75],'DefaultAxesYLim',[0,0.75],...
    'DefaultRectangleEdgeColor','none');
  for i=1:24, subplot(4,6,i); box on
    j=find(vc==c(i)); title(name{j});
    dark=LocalBrighten(cn(j,:),-beta);
    light=LocalBrighten(cn(j,:),beta);
    rectangle('Position',[0,0.00,0.75,0.25],'FaceColor',dark);
    rectangle('Position',[0,0.25,0.75,0.25],'FaceColor',cn(j,:));
    rectangle('Position',[0,0.50,0.75,0.25],'FaceColor',light);
    rectangle('Position',[0,0.00,0.75,0.75],'EdgeColor','k');
    if rem(i,6)==1
      set(gca,'YTickLabel',{'dark','normal','light'},...
        'YTick',[0.125,0.375,0.625]);
      if i==19
        text(0,-0.25,['{\bf\itARROW3}  Named Color Table  ',...
            '( \beta = ',num2str(beta),' )']);
      end
    end
  end
end

%-------------------------------------------------------------------------
% ColorMap
function [C,failed]=LocalColorMap(c,vc,cn,beta)
n=length(c); failed=0; C=zeros(n,3); i=1; j=1;
while 1
  if ~sum([vc,'_^']==c(i)), failed=1; break, end
  if sum('_^'==c(i))
    if i+1>n, failed=1; break, end
    if ~sum(vc==c(i+1)), failed=1; break, end
    cc=cn(vc==c(i+1),:); gamma=beta;
    if c(i)=='_', gamma=-beta; end
    C(j,:)=LocalBrighten(cc,gamma); i=i+2;
  else, C(j,:)=cn(vc==c(i),:); i=i+1;
  end
  if i>n, break, end, j=j+1;
end
if n>j, C(j+1:n,:)=[]; end

%-------------------------------------------------------------------------
% Brighten
function C=LocalBrighten(c,beta)
if sum([c==0,c==1])==3 && sum(c==0)<3 && sum(c==1)<3
  if beta<0
    C=(1+beta)*c;
  else
    C=c;  C(C==0)=beta;
  end
else
  C=c.^((1-min(1-sqrt(eps),abs(beta)))^sign(beta));
end

%-------------------------------------------------------------------------
% Repmat
function B=LocalRepmat(A,siz)
if length(A)==1, B(prod(siz))=A; B(:)=A; B=reshape(B,siz);
else, [m,n]=size(A); mind=(1:m)'; nind=(1:n)';
  mind=mind(:,ones(1,siz(1))); nind=nind(:,ones(1,siz(2)));
  B=A(mind,nind);
end

%-------------------------------------------------------------------------
% Interp
function v=LocalInterp(xmin,xmax,y,u)
[m,n]=size(y); h=(xmax-xmin)/(m-1); p=length(u); v=zeros(p,n);
k=min(max(1+floor((u-xmin)/h),1),m-1); s=(u-xmin)/h-k+1;
for j=1:n, v(:,j)=y(k,j)+s.*(y(k+1,j)-y(k,j)); end
v(v<0)=0; v(v>1)=1;

%-------------------------------------------------------------------------
% Check for supported log scales
function [xs,ys,xys]=LocalLogCheck(ax)
xs=strcmp(get(ax,'xscale'),'log');
ys=strcmp(get(ax,'yscale'),'log');
zs=strcmp(get(ax,'zscale'),'log');
if zs, error('Z log scale not supported'), end
xys=xs+ys;
if xys, azel=get(ax,'view');
  if abs(azel(2))~=90, error('3D log plot not supported'), end
end

%-------------------------------------------------------------------------
% Generate valid value for color, linestyle and linewidth
function [c,ls,lw]=LocalValidateCLSW(s)
if nargin<1 || isempty(s), c='k'; ls='-'; lw=0.5;
else
  % identify linestyle
  if contains(s,'--'), ls='--'; s=strrep(s,'--','');
  elseif contains(s,'-.'), ls='-.'; s=strrep(s,'-.','');
  elseif contains(s,'-'), ls='-'; s=strrep(s,'-','');
  elseif contains(s,':'), ls=':'; s=strrep(s,':','');
  elseif contains(s,'*'), ls='*'; s=strrep(s,'*','');
  else, ls='-';
  end

  % identify linewidth
  tmp=double(s);
  tmp=find(tmp>45 & tmp<58);
  if ~isempty(tmp)
    if any(s(tmp)=='/'), lw='/'; else, lw=str2double(s(tmp)); end
    s(tmp)='';
  else, lw=0.5;
  end

  % identify color
  if ~isempty(s), s=lower(s);
    if length(s)>1, c=s(1:2);
    else, c=s(1); end
  else, c='k';
  end
end


function exit_reason = checkSimulationStopConditions(n_cells, Phis_t, cs_bar_t, T_t, param)
% checkSimulationStopConditions checks if a particular stop condition for the simulation is met

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

exit_reason = 0;
% Check stop conditions for each cell
for i=1:n_cells
    Phis_pos_cc_t = 1.5*Phis_t{i}(end,1) - 0.5*Phis_t{i}(end,2);
    Phis_neg_cc_t = 1.5*Phis_t{i}(end,end) - 0.5*Phis_t{i}(end,end-1);
    voltage = Phis_pos_cc_t - Phis_neg_cc_t;
    Sout    = internalSOCestimate(cs_bar_t,param,i);
    max_layer_temperature = max(T_t{1}(end,:));
    % Break conditions.
    if(voltage<param{i}.CutoffVoltage)
        if param{i}.suppress_status_prints == 0
            fprintf('\nCell #%d  below its Cutoff voltage. Stopping ...\n',i);
        end
        if exit_reason==0
            exit_reason=[];
        end
        exit_reason = [exit_reason;1];
    end

    if(voltage>param{i}.CutoverVoltage)
        if param{i}.suppress_status_prints == 0
            fprintf('\nCell #%d  above its Cutover voltage. Stopping ...\n',i);
        end
        if exit_reason==0
            exit_reason=[];
        end
        exit_reason = [exit_reason;2];
    end

    if(Sout<param{i}.CutoffSOC)
        if param{i}.suppress_status_prints == 0
            fprintf('\nCell #%d  below its Cutoff SOC. Stopping ...\n',i);
        end
        if exit_reason==0
            exit_reason=[];
        end
        exit_reason = [exit_reason;3];
    end

    if(Sout>param{i}.CutoverSOC)
        if param{i}.suppress_status_prints == 0
            fprintf('\nCell #%d  above its Cutover SOC. Stopping ...\n',i);
        end
        if exit_reason==0
            exit_reason=[];
        end
        exit_reason = [exit_reason;4];
    end

    if(max_layer_temperature > 0.99*param{i}.Tmax)
        if param{i}.suppress_status_prints == 0
            fprintf('\nCell #%d  above its Maximum Permitted Temperature. Stopping ...\n',i);
        end
        if exit_reason==0
            exit_reason=[];
        end
        exit_reason = [exit_reason;5];
    end

    if(param{i}.enable_csneg_Saturation_limit == 1)
        if(param{i}.SolidPhaseDiffusion~=3)
            max_cs_surface_neg = max(cs_bar_t{i}(end,param{i}.Np+1:end));
        else
            max_cs_surface_neg = max(cs_bar_t{i}(end, (param{i}.Np*param{i}.Nr_p) +1:end));
        end

        if (max_cs_surface_neg > param{i}.cs_sat_thresh*param{i}.cs_neg_saturation)
            if param{i}.suppress_status_prints == 0
                fprintf('\n\nCell #%d  above its safe surface concentration (saturation threshold) Stopping ...\n\n',i);
            end
            if exit_reason==0
                exit_reason=[];
            end
            exit_reason = [exit_reason;6];
        end
    end
end
end


function [HDR]=openxml(arg1,CHAN,arg4,arg5,arg6)
% OPENXML reads XML files and tries to extract biosignal data
%
% This is an auxilary function to SOPEN.
% Use SOPEN instead of OPENXML.
%

%
% HDR = openxml(HDR);
%
% HDR contains the Headerinformation and internal data
%
% see also: SOPEN, SREAD, SSEEK, STELL, SCLOSE, SWRITE, SEOF

% This program is free software; you can redistribute it and/or
% modify it under the terms of the GNU General Public License
% as published by the Free Software Foundation; either version 3
% of the License, or (at your option) any later version.

%	$Id$
%	Copyright 2006,2007,2008 by Alois Schloegl <a.schloegl@ieee.org>
%    	This is part of the BIOSIG-toolbox http://biosig.sf.net/

if ischar(arg1);
        HDR.FileName = arg1;
        HDR.FILE.PERMISSION = 'r';
else
        HDR = arg1;
end;

%if strncmp(HDR.TYPE,'XML',3),
%        if any(HDR.FILE.PERMISSION=='r'),
                fid = fopen(HDR.FileName,HDR.FILE.PERMISSION,'ieee-le');
                s = char(fread(fid,[1,1024],'char'));
                if all(s(1:2)==[255,254]) & all(s(4:2:end)==0)
                        HDR.TYPE='XML-UTF16';
                elseif ~isempty(findstr(char(s),'?xml version'))
                        HDR.TYPE='XML-UTF8';
		end;
		fseek(fid,0,'bof');
                if strcmp(HDR.TYPE,'XML-UTF16'),
                        magic = char(fread(fid,1,'uint16'));
                        HDR.XML = char(fread(fid,[1,inf],'uint16'));
                elseif strcmp(HDR.TYPE,'XML-UTF8'),
                        HDR.XML = char(fread(fid,[1,inf],'uint8'));
                end;
                fclose(fid);
                HDR.FILE.FID = fid;

                if 0, ~exist('xmlstruct')
                        warning('XML toolbox missing')
                end;
                if 1,
                        HDR.XMLstruct = xmlstruct(HDR.XML,'sub');
                        HDR.XMLlist   = xmlstruct(HDR.XML);
                end;

                try
                        XML = xmltree(HDR.XML);
                        XML = convert(XML);
                        HDR.XML  =  XML;
			HDR.TYPE = 'XML';
                catch
                        fprintf(HDR.FILE.stderr,'ERROR SOPEN (XML): XML-toolbox missing or invalid XML file.\n');
                        return;
                end;

                tmp = fieldnames(HDR.XML);
                if any(strmatch('PatientDemographics',tmp)) & any(strmatch('TestDemographics',tmp)) & any(strmatch('RestingECGMeasurements',tmp)) & any(strmatch('Diagnosis',tmp)) & any(strmatch('Waveform',tmp)),
                        % GE-Marquette FDA-XML   MAC5000

                        HDR.Patient.ID = HDR.XML.PatientDemographics.PatientID;
                        tmp = HDR.XML.PatientDemographics.Gender;
                        HDR.Patient.Sex = 2*(upper(tmp(1))=='F') + (upper(tmp(1))=='M');
                        HDR.Patient.Name = [HDR.XML.PatientDemographics.PatientLastName,', ',HDR.XML.PatientDemographics.PatientFirstName];

                        tmp = HDR.XML.TestDemographics.AcquisitionDate;
                        tmp(tmp=='-') = ' ';
                        HDR.T0([3,2,1])=str2double(tmp);
                        tmp = HDR.XML.TestDemographics.AcquisitionTime;
                        tmp(tmp==':') = ' ';
                        HDR.T0(4:6) = str2double(tmp);

                        HDR.NS = str2double(HDR.XML.Waveform.NumberofLeads);
                        HDR.SampleRate = str2double(HDR.XML.Waveform.SampleBase);
                        HDR.Filter.LowPass  = str2double(HDR.XML.Waveform.LowPassFilter)*ones(1,HDR.NS);
                        HDR.Filter.HighPass = str2double(HDR.XML.Waveform.HighPassFilter)*ones(1,HDR.NS);
                        HDR.Filter.Notch    = str2double(HDR.XML.Waveform.ACFilter)*ones(1,HDR.NS);

                        HDR.NRec = 1;
                        HDR.SPR = 1;
                        for k = 1:HDR.NS,
                                CH = HDR.XML.Waveform.LeadData{k};
                                HDR.AS.SPR(k)  = str2double(CH.LeadSampleCountTotal);
                                HDR.SPR        = lcm(HDR.SPR,HDR.AS.SPR(k));
                                HDR.Cal(k)     = str2double(CH.LeadAmplitudeUnitsPerBit);
                                HDR.PhysDim{k} = CH.LeadAmplitudeUnits;
                                HDR.Label{k}   = CH.LeadID;

                                t = radix64d(CH.WaveFormData);
                                t = 256*t(2:2:end) + t(1:2:end);
                                t = t - (t>=(2^15))*(2^16);
                                HDR.data(:,k) = t(:);
                        end;
                        HDR.TYPE = 'native';

                elseif any(strmatch('component',tmp))
                          % FDA-XML Format
                        tmp   = HDR.XML.component.series.derivation;
                        if isfield(tmp,'Series');
                                tmp = tmp.Series.component.sequenceSet.component;
                        else    % Dovermed.CO.IL version of format
                                tmp = tmp.derivedSeries.component.sequenceSet.component;
                        end;
                        HDR.NS = length(tmp)-1;
                        HDR.NRec = 1;
                        HDR.Cal = 1;
                        HDR.PhysDim = {' '};
                        HDR.SampleRate = 1;
                        HDR.TYPE = 'XML-FDA';     % that's an FDA XML file

                elseif any(strmatch('dataacquisition',tmp)) & any(strmatch('reportinfo',tmp)) & any(strmatch('patient',tmp)) & any(strmatch('documentinfo',tmp)),
                        % SierraECG  1.03  *.open.xml from PHILIPS
                        HDR.SampleRate = str2double(HDR.XML.dataacquisition.signalcharacteristics.samplingrate);
                        HDR.NS  = str2double(HDR.XML.dataacquisition.signalcharacteristics.numberchannelsvalid);
                        HDR.Cal = str2double(HDR.XML.reportinfo.reportgain.amplitudegain.overallgain);
                        HDR.PhysDim = {'uV'};
                        HDR.Filter.HighPass = str2double(HDR.XML.reportinfo.reportbandwidth.highpassfiltersetting);
                        HDR.Filter.LowPass  = str2double(HDR.XML.reportinfo.reportbandwidth.lowpassfiltersetting);
                        HDR.Filter.Notch    = str2double(HDR.XML.reportinfo.reportbandwidth.notchfiltersetting);

                        t = HDR.XML.reportinfo.reportformat.waveformformat.mainwaveformformat;
                        k = 0;
                        HDR.Label={};
                        while ~isempty(t),
                                [s,t] = strtok(t,' ');
                                k = k+1;
                                HDR.Label{k, 1} = [s,' '];
                        end;
                        HDR.Patient.Id     = str2double(HDR.XML.patient.generalpatientdata.patientid);
                        tmp    = HDR.XML.patient.generalpatientdata.age;
                        if isfield(tmp,'years'),
                                HDR.Patient.Age    = str2double(tmp.years);
                        end
                        if isfield(tmp,'dateofbirth')
                                tmp = tmp.dateofbirth;
                                tmp(tmp=='-')=' ';
                                HDR.Patient.Birthday([6,5,4]) = str2double(tmp);
                        end;

                        tmp    = HDR.XML.patient.generalpatientdata.sex;
                        HDR.Patient.Sex    = strncmpi(tmp,'Male',1) + strncmpi(tmp,'Female',1)*2;
                        HDR.Patient.Weight = str2double(HDR.XML.patient.generalpatientdata.weight.kg);
                        HDR.Patient.Height = str2double(HDR.XML.patient.generalpatientdata.height.cm);

                        HDR.VERSION = HDR.XML.documentinfo.documentversion;
                        HDR.TYPE = HDR.XML.documentinfo.documenttype;

                elseif any(strmatch('component',tmp)) & any(strmatch('reportinfo',tmp)) & any(strmatch('patient',tmp)) & any(strmatch('documentinfo',tmp)),
                           % FDA-XML Format
                        tmp   = HDR.XML.component.series.derivation;
                        if isfield(tmp,'Series');
                                tmp = tmp.Series.component.sequenceSet.component;
                        else    % Dovermed.CO.IL version of format
                                tmp = tmp.derivedSeries.component.sequenceSet.component;
                        end;
                        HDR.NS = length(tmp)-1;
                        HDR.NRec = 1;
                        HDR.Cal = 1;
                        HDR.PhysDim = {' '};
                        HDR.SampleRate = 1;
                        HDR.TYPE = 'XML-FDA';     % that's an FDA XML file

                elseif any(strmatch('StripData',tmp)) & any(strmatch('ClinicalInfo',tmp)) & any(strmatch('PatientInfo',tmp)) & any(strmatch('ArrhythmiaData',tmp)),
                        % GE Case8000 stress ECG

                	HDR.SampleRate = str2double(HDR.XML.StripData.SampleRate);
                	tmp = HDR.XML.ClinicalInfo.ObservationDateTime;
                	HDR.T0 = [str2double(tmp.Year), str2double(tmp.Month), str2double(tmp.Day), str2double(tmp.Hour), str2double(tmp.Minute), str2double(tmp.Second)];

                	HDR.Patient.Id = HDR.XML.PatientInfo.PID;
                	HDR.Patient.Name = 'X'; % [HDR.XML.PatientInfo.Name, ', ', HDR.XML.PatientInfo.GivenName];
                	HDR.Patient.Age = str2double(HDR.XML.PatientInfo.Age);
                	tmp = HDR.XML.PatientInfo.Gender;
                	HDR.Patient.Sex = any(tmp(1)=='Mm') + any(tmp(1)=='Ff')*2;
                	HDR.Patient.Height = str2double(HDR.XML.PatientInfo.Height);
                	HDR.Patient.Weight = str2double(HDR.XML.PatientInfo.Weight);
                	tmp = HDR.XML.PatientInfo.BirthDateTime;
                	HDR.Patient.Birthday = [str2double(tmp.Year), str2double(tmp.Month), str2double(tmp.Day),0,0,0];

                	tmp = HDR.XML.StripData.Strip;
                	HDR.NS = length(tmp{1}.WaveformData);
               		tmax   = str2double(tmp{end}.Time.Minute)*60 + str2double(tmp{end}.Time.Second)+10;
                	data   = repmat(NaN,tmax*HDR.SampleRate,HDR.NS);
                	for k  = 1:length(tmp);
	                	t = HDR.SampleRate*(str2double(tmp{k}.Time.Minute)*60 + str2double(tmp{k}.Time.Second));
	                	for k2 = 1:HDR.NS,
		                	x = str2double(tmp{k}.WaveformData{k2});
		                	data(t+1:t+length(x),k2)=x(:);
	                	end;
                	end;
                	tmp = HDR.XML.ArrhythmiaData.Strip;
                	for k  = 1:length(tmp);
	                	t = HDR.SampleRate*(str2double(tmp{k}.Time.Minute)*60 + str2double(tmp{k}.Time.Second));
	                	for k2 = 1:HDR.NS,
		                	x = str2double(tmp{k}.WaveformData{k2});
		                	data(t+1:t+length(x),k2)=x(:);
	                	end;
                	end;
                	HDR.data = data - 2^12*(data>2^11);
                	HDR.TYPE = 'native';
                	HDR.NRec = 1;
                	HDR.SPR  = size(HDR.data,1);
                	HDR.Calib = sparse(2:HDR.NS,1:HDR.NS,1);
                	HDR.FLAG.UCAL = 1;

                else
                        fprintf(HDR.FILE.stderr,'Warning SOPEN (XML): File %s is not supported.\n',HDR.FileName);
                        return;
                end

                try
                	tmp=HDR.XML.componentOf.timepointEvent.componentOf.subjectAssignment.subject.trialSubject.subjectDemographicPerson.name;
                	HDR.Patient.Name = sprintf('%s, %s',tmp.family, tmp.given);
		catch
                end;

                HDR.Calib = sparse(2:HDR.NS+1,1:HDR.NS,HDR.Cal);
                HDR.FILE.OPEN = 1;
                HDR.FILE.POS  = 0;
%        end;
%end;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Auxillary functions
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function y = radix64d(x);
% RADIX64D - decoding of radix64 encoded sequence

% 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.

%	$Id$
%	(C) 2006 by Alois Schloegl <a.schloegl@ieee.org>
%    	This is part of the BIOSIG-toolbox http://biosig.sf.net/

global BIOSIG_GLOBAL

if ~isfield(BIOSIG_GLOBAL,'R64E');
	BIOSIG_GLOBAL.R64E = ['A':'Z','a':'z','0':'9','+','/'];
	BIOSIG_GLOBAL.R64D = zeros(256,1)-1;
	for k = 1:length(BIOSIG_GLOBAL.R64E),
		BIOSIG_GLOBAL.R64D(BIOSIG_GLOBAL.R64E(k)) = k-1;
	end;
end;

% http://www.faqs.org/rfcs/rfc2440.html

t = BIOSIG_GLOBAL.R64D(x);
t = [t(t>=0); zeros(3,1)];
N = floor(length(t)/4);
t = reshape(bitand(t(1:N*4),2^6-1),4,N);

y(1,:) = bitshift(t(1,:),2)         + bitshift(t(2,:),-4);
y(2,:) = bitshift(mod(t(2,:),16),4) + bitshift(t(3,:),-2);
y(3,:) = bitshift(mod(t(3,:),4),6)  + t(4,:);

y = y(:)';
y = y(1:end-sum(x=='=')); % remove possible pad characters


function dicomrt_plotctramp(name,materials,mat_ct_up_bound,density_lo_bound,density_up_bound)
% dicomrt_plotctramp(name,materials,mat_ct_up_bound,density_lo_bound,density_up_bound)
%
% Plot ct conversion ramp used to create ct-phantom.
%
% materials			    materials name
% mat_ct_up_bound		materials upper bound (see dosxyz user's manual)
% density_lo_bound		density lower bound (see dosxyz user's manual)
% density_up_bound		density upper bound (see dosxyz user's manual)
%
% See also dicomrt_ctcreate, dicomrt_createwphantom
%
% Copyright (C) 2002 Emiliano Spezi (emiliano.spezi@physics.org)

% Plot
figure
set(gcf,'Name',['dicomrt_plotctramp: CT conversion ramp for ',name]);
hold
for i=1:size(materials,1)
    if i==1
        plot([0 mat_ct_up_bound(i)], [0 density_up_bound(i)],'LineWidth',2);
        text(mat_ct_up_bound(i)/2,density_up_bound(i)/2,['\leftarrow', materials(i,:)],'FontSize',12,'FontWeight','bold');
    else
        locateXtext=(mat_ct_up_bound(i)-mat_ct_up_bound(i-1))./2+mat_ct_up_bound(i-1);
        locateYtext=(density_up_bound(i)-density_lo_bound(i))./2+density_lo_bound(i);
        plot([mat_ct_up_bound(i-1) mat_ct_up_bound(i)], [density_lo_bound(i) density_up_bound(i)],...
            'LineWidth',2);
        plot(mat_ct_up_bound(i-1),density_lo_bound(i),'Linestyle','none','Marker','d','MarkerFaceColor','auto');
        text(locateXtext,locateYtext,['  \leftarrow ', materials(i,:)],'FontSize',12,'FontWeight','bold');
    end
end
grid on
title('CT conversion ramp','FontSize',18);
ylabel('Density (g/cm^3)','FontSize',14);
xlabel('CT number','FontSize',14);
set(gca,'XLim',[0 max(mat_ct_up_bound)])
set(gca,'YLim',[0 max(density_up_bound)])


function[p] = elec_load_brainstorm(p)

% elec_load_brainstorm - Load brainstorm Channel data into eeg_toolbox
%
% Usage: [p] = elec_load_brainstorm(p)
%
% where:    p.elec.path & p.elec.file define the brainsform file
%
% The brainstorm file consists of Channel, which is an array of
% structures.  The fields are:
%
%     Loc     - a 3x2 matrix of electrode coordinates (x,y,z in rows).
%               BrainStorm (x,y,z meters) = 3Dspace (x,y,z cm) / 100.
%     Orient  - a corresponding matrix of sensor orientations (MEG);
%               all zero for EEG.
%     Weight  - a vector of relative or absolute weights (eg, amplification);
%               all ones for this routine.
%     Type    - a character string, 'EEG' in this case.
%     Name    - a charater string indicating the electrode name.
%     Comment - a charater string indicating the reference electrode.  Empty
%               for active electrodes and 'EEG REF' for reference electrode.
%
% See brainstorm website at http://neuroimage.usc.edu/, including a
% download pdf file describing the brainstorm database formats.
%

% $Revision: 1.1 $ $Date: 2009-04-28 22:13:55 $

% Licence:  GNU GPL, no express or implied warranties
% History:  11/2002, Darren.Weber_at_radiology.ucsf.edu
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if ~exist('p', 'var'), error('...no p struct input\n');
elseif isempty(p),     error('...no p struct input\n');
end

fprintf('\nELEC_LOAD_BRAINSTORM...\n'); tic;

fprintf('...Converting brainstorm to p structure.\n');

[path,file,ext] = fileparts(strcat(p.elec.path,filesep,p.elec.file));
bsfile = fullfile(path,[file ext]);

load(bsfile, 'Channel');

for i=1:length(Channel),

    if findstr(lower(Channel(i).Comment),'ref'),
        p.elec.data.ref = Channel(i).Loc(:,1)';
    else
        p.elec.data.x(i,1) = Channel(i).Loc(1,1);
        p.elec.data.y(i,1) = Channel(i).Loc(2,1);
        p.elec.data.z(i,1) = Channel(i).Loc(3,1);
        p.elec.data.label{i,1} = Channel(i).Name;
    end
end

p.elec.data.centroid = [0 0 0];
p.elec.data.nasion = [];
p.elec.data.lpa = [];
p.elec.data.rpa = [];

t = toc; fprintf('...done (%6.2f sec).\n\n',t);

return


function model = fgplvmAddConstraint(model,options,varargin)

% FGPLVMADDCONSTRAINT Add latent constraints to FGPLVM model
% FORMAT
% DESC Adds constraint structure to FGPLVM model
% ARG model : fgplvm model
% ARG options : options strucure as returnded from
% constraintOptions
% RETURN model : the GP-LVM model.
%
% COPYRIGHT : Carl Henrik Ek, 2009
%
% SEEALSO : constraintOptions

% FGPLVM

%
if(isfield(model,'constraints')&&~isempty(model.constraints))
  % constraints init
  model = constraintCreate(model,[],options,varargin{:});
else
  % no constraints
  model.constraints = [];
  model.constraints.q = model.q;
  model.constraints.N = model.N;
  model.constraints.id = [];
  model.constraints.numConstraints = 0;
  model.constraints.comp = {};
  model = constraintCreate(model,[],options,varargin{:});
end

model.constraints.numConstraints = model.constraints.numConstraints + 1;
model.constraints.id = [model.constraints.id; false*ones(1,model.q)];
if(~isempty(options.dim))
  model.constraints.id(end,options.dim) = true;
else
  warning('No Dimensions set for constraints, applying to full latent space');
  model.constraints.id(end,:) = true;
end

% update constraints
for(i = 1:1:model.constraints.numConstraints)
  model.constraints.comp{i} = constraintExpandParam(model.constraints.comp{i},model.X);
end


function rv = run_userfcn(userfcn, stage, varargin)
%RUN_USERFCN   Runs the userfcn callbacks for a given stage.
%   RV = RUN_USERFCN(USERFCN, STAGE, VARARGIN)
%
%   USERFCN : the 'userfcn' field of mpc, populated by ADD_USERFCN
%   STAGE   : the name of the callback stage being executed
%   (additional arguments) some stages require additional arguments.
%
%   Example:
%       mpc = om.get_mpc();
%       om = run_userfcn(mpc.userfcn, 'formulation', om);
%
%   See also ADD_USERFCN, REMOVE_USERFCN, TOGGLE_RESERVES, TOGGLE_IFLIMS,
%   RUNOPF_W_RES.

%   MATPOWER
%   Copyright (c) 2009-2016, Power Systems Engineering Research Center (PSERC)
%   by Ray Zimmerman, PSERC Cornell
%
%   This file is part of MATPOWER.
%   Covered by the 3-clause BSD License (see LICENSE file for details).
%   See https://matpower.org for more info.

rv = varargin{1};
if ~isempty(userfcn) && isfield(userfcn, stage)
    for k = 1:length(userfcn.(stage))
        if isfield(userfcn.(stage)(k), 'args')
            args = userfcn.(stage)(k).args;
        else
            args = [];
        end
        rv = feval(userfcn.(stage)(k).fcn, rv, varargin{2:end}, args);
        % mpc     = userfcn_*_ext2int(mpc, mpopt, args);
        % om      = userfcn_*_formulation(om, mpopt, args);
        % results = userfcn_*_int2ext(results, mpopt, args);
        % results = userfcn_*_printpf(results, fd, mpopt, args);
        % mpc     = userfcn_*_savecase(mpc, fd, prefix, args);
    end
end


% JM: 2006/07/19
% viewer for data Tc on domain Omega

function varargout = viewImage(varargin);
% this is just output and hence not commented; fell free to improve (-;
% function varargout = viewImage(Tc,Omega,m,varargin);

persistent PARA
% -----------------------------------------------------------------------------
if nargin == 0,
  varargout{1} = PARA;
  return;
end;
% set persistent parameter
if nargin >= 1 & strcmp(varargin{1},'set'),

  if strcmp(varargin{2},'para'),
    viewPara = varargin{3};
    fn = fieldnames(viewPara);
    for j=1:length(fn),
      para{2*j-1} = fn{j};
      para{2*j}   = getfield(viewPara,fn{j});
    end;
    viewImage('set',para{:})
    return;
  end;

  % default parameter
  fprintf('%s: set persistent parameter\n',mfilename)
  PARA.viewer   = [];
  PARA.figname  = [];
  PARA.fig      = 1;
  PARA.sub      = [1,1,1];
  PARA.name     = '';
  PARA.plots    = 1;
  PARA.scale    = 0;
  PARA.invert   = 0;

  varargin(1) = [];
  % overwrite default parameter
  for j=1:length(varargin)/2,
    field = varargin{2*j-1};
    value = varargin{2*j};
    PARA  = setfield(PARA,field,value);
  end;
  if nargout > 0, varargout{1} = PARA;  end;
  return;
end;
% -----------------------------------------------------------------------------

% -----------------------------------------------------------------------------
% start to work

Tc    = varargin{1};
Omega = varargin{2};
m     = varargin{3};
varargin(1:3) = [];

% start with global parameters and update
para = PARA;
for j=1:length(varargin)/2,
  field = varargin{2*j-1};
  value = varargin{2*j};
  para  = setfield(para,field,value);
end;

if ~para.plots, return;  end;          % do nothing, just return;

% build parameter list
fields = fieldnames(para);
for j=1:length(fields);
  optn{2*j-1} = fields{j};
  optn{2*j}   = getfield(para,fields{j});
end;

if isfield(para,'scale') & para.scale,
  Tc = 255/max(Tc)*Tc;
end;

if isfield(para,'invert') & para.invert,
  Tc = max(Tc)-Tc;
end;

% setup figure

fig = figure(para.fig);
subplot(para.sub(1),para.sub(2),para.sub(3)); cla;

if ~isempty(para.figname),
  set(fig,'numbertitle','off','name',sprintf('[JM-%d]: %s',fig,para.figname));
end;

% call the viewer

fh = feval(para.viewer,Tc,Omega,m,optn{:});

str = sprintf('%s',para.name);
title(str);

if nargout>0,
  varargout{1} = fh;
end;

return;
%
% if nargout>0,  varargout{1} = fh;  end;
% return;
% %==============================================================================
%
%
% function p = volview(I,Omega,m,varargin)
%
% I = reshape(I,m);
% % I = flipdim(I,3);
% isovalue    = 0;
% viewpoint   = [-37.5,30];
% facecolor   = .75*[1,1,1];
% facealpha   = .8;
%
% %% overwrite default parameter
% for k=1:1:length(varargin)/2,
%   %disp([varargin{2*k-1},'=varargin{',int2str(2*k),'};']);
%   eval([varargin{2*k-1},'=varargin{',int2str(2*k),'};']);
% end;
%
% I(I<isovalue) = 0;
%
% p(1) = patch(isosurface(I,isovalue));
%
% switch computer,
% case 'PCWIN',
%   set(p(1),...
%     'FaceColor',facecolor,...
%     'EdgeColor','none');
% otherwise,
%   set(p(1),...
%     'FaceColor',facecolor,...
%     'EdgeColor','none',...
%     'FaceAlpha',facealpha);
% end;
%
% isonormals(I,p(1))
%
% p(2) = patch(isocaps(I,isovalue));
%
% set(p(2),...
%   'FaceColor','interp',...
%   'EdgeColor','none');
%
% view(viewpoint);
% axis equal
% axis([1,size(I,2),1,size(I,1),1,size(I,3)]);
% camlight; lighting phong
% material dull
% drawnow
% %==============================================================================
%
% %==============================================================================
% function ih = montage(I,Omega,m,varargin)
%
% I = reshape(I,m);
%
% threshold = 0;
% framesx   = [];
% framesy   = [];
% direction = 'x';
% number    = 'off';
% colmap    = [];
% %% overwrite default parameter
% for k=1:1:length(varargin)/2,
%   %disp([varargin{2*k-1},'=varargin{',int2str(2*k),'};']);
%   eval([varargin{2*k-1},'=varargin{',int2str(2*k),'};']);
% end;
%
% if threshold ~= 0,
%   I(I<threshold) = 0;
% end;
%
% I = squeeze(I);
% l = length(size(I));
%
% fig = gcf;
% switch l,
%   case 1,
%     hh = plot(1:size(I,1),I);
%   case 2,
%     hh = subimage(I,colmap);
%     if ~isempty(colmap), colormap(colmap); end;
%   case 3,
%     [m1,m2,m3] = size(I);
%
%     switch direction,
%       case 'x',
%         aa = m1; b1 = m2; b2 = m3; I1 = 1:m2; I2 = 1:m3;
%       case 'y',
%         aa = m2; b1 = m1; b2 = m3; I1 = 1:m1; I2 = 1:m3;
%       case 'z',
%         aa = m3; b1 = m1; b2 = m2; I1 = 1:m1; I2 = 1:m2;
%
%       otherwise, jmerror(direction);
%     end;
%
%     if isempty(framesx) & isempty(framesy),
%       framesx = ceil(sqrt(aa));
%       framesy = ceil(aa/framesx);
%     elseif isempty(framesx),
%       framesx = ceil(aa/framesy);
%     else
%       framesy = ceil(aa/framesx);
%     end;
%
%     %   fprintf('image is %dx%dx%d, frames [%d,%d]\n',...
%     %     m1,m2,m3,framesx,framesy);
%
%     B = zeros(b1*framesx,b2*framesy);
%
%     a1 = 0; a2 = 0;
%     for j=1:aa,
%       switch direction,
%         case 'x',  P = squeeze(I(j,:,:));
%         case 'y',  P = squeeze(I(:,j,:));
%         case 'z',  P = squeeze(I(:,:,j));
%       end;
%       B(a1+I1,a2+I2) = P;
%       a2 = a2 + b2;
%       if ~rem(j,framesy), a1 = a1 + b1; a2 = 0; end;
%     end;
%
%     ih = image(B);
%     if ~isempty(colmap), colormap(colmap); end;
%     hold on
%
%     if strcmp(number,'on'),
%       a1 = 0; a2 = 0;
%       for j=1:aa,
%         text(a2+10,a1+20,['#',int2str(j)],'fontsize',5,'color','r');
%         a2 = a2 + b2;
%         if ~rem(j,framesy), a1 = a1 + b1; a2 = 0; end;
%       end;
%     end;
%
%     for j=0:framesx,
%       plot(0.5+[0,framesy*b2],0.5+b1*j*[1,1],'b-');
%     end;
%     for j=0:framesy,
%       plot(0.5+b2*j*[1,1],0.5+[0,framesx*b1],'b-');
%     end;
%
%     axis equal
%     axis([0 framesy*b2+1, 0, framesx*b1+1])
%     axis off
%     hold off;
%   otherwise,
%     jmerror('dimension=?');
% end;
% % ==============================================================================
%
% %==============================================================================
function  PARA = setPARA(varargin)
%fprintf('%s\n',mfilename)
PARA = [];

% overwrite default parameter
for k=1:1:length(varargin)/2,
  field = varargin{2*k-1};
  value = varargin{2*k};
  PARA = setfield(PARA,field,value);
end;


function pneopt = mpopt2pneopt(mpopt, model, alg)
%MPOPT2PNEOPT   Create/modify PNES_MASTER options struct from MPOPT.
%
%   PNEOPT = MPOPT2PNEOPT(MPOPT, MODEL)
%   PNEOPT = MPOPT2PNEOPT(MPOPT, MODEL, ALG)
%
%   Uses a MATPOWER options struct, MPOPT, to create or modify an
%   PNES_MASTER options struct.
%
%   Inputs (default values in parentheses):
%       MPOPT : MATPOWER options struct
%       MODEL ('PNE') : (optional) one of the following model types, required
%               for selection of solver in case ALG is 'DEFAULT' (solver
%               precedence for each model type list in parentheses):
%           'PNE' - nonlinear equation with all continuous variables
%                   (NEWTON, FSOLVE)
%       ALG ('DEFAULT') : (optional) any valid value of OPT.alg for
%               PNES_MASTER.
%
%   Output:
%       PNEOPT : an options struct for use by PNES_MASTER and friends
%
%   See PNES_MASTER, MPOPTION.

%   MP-Opt-Model
%   Copyright (c) 2015-2021, Power Systems Engineering Research Center (PSERC)
%   by Ray Zimmerman, PSERC Cornell
%
%   This file is part of MP-Opt-Model.
%   Covered by the 3-clause BSD License (see LICENSE file for details).
%   See https://github.com/MATPOWER/mp-opt-model for more info.

%% set default args
if nargin < 3
    alg = 'DEFAULT';
    if nargin < 2
        model = '';
    end
end

%% use pf.alg to set nleqs_opt for corrector
switch mpopt.pf.alg
    case 'FSOLVE'
        nleqs_alg = 'FSOLVE';
    otherwise
        nleqs_alg = 'NEWTON';
end

%% create PNES_MASTER options struct
pneopt = struct( ...
    'alg',              alg, ...
    'verbose',          mpopt.verbose, ...
    'nleqs_opt',        struct( ...
        'alg',              nleqs_alg, ...
        'tol',              mpopt.pf.tol    ), ...
    'stop_at',          mpopt.cpf.stop_at, ...
    'solve_base',       1, ...
    'parameterization', mpopt.cpf.parameterization, ...
    'step',             mpopt.cpf.step, ...
    'step_min',         mpopt.cpf.step_min, ...
    'step_max',         mpopt.cpf.step_max, ...
    'adapt_step',       mpopt.cpf.adapt_step, ...
    'adapt_step_damping',   mpopt.cpf.adapt_step_damping, ...
    'adapt_step_tol',   mpopt.cpf.adapt_step_tol, ...
    'target_lam_tol',   mpopt.cpf.target_lam_tol, ...
    'nose_tol',         mpopt.cpf.nose_tol, ...
    'output_fcn',       @(varargin)cpf_pne_output_fcn(obj, ad, varargin{:}), ...
    'plot',             struct( ...
        'level',            mpopt.cpf.plot.level, ...
        'idx',              mpopt.cpf.plot.bus, ...
        'idx_default',      @()cpf_plot_idx_default(obj, dm, ad), ...
        'yname',            'V', ...
        'yfcn',             @(v_,idx)cpf_plot_yfcn(obj, dm, ad, v_, idx), ...
        'title',            'Voltage at Bus %d', ...
        'title2',           'Voltage at Multiple Buses', ...
        'ylabel',           'Voltage Magnitude', ...
        'legend',           'Bus %d'    ) ...
);


% The COBRAToolbox: testDynamicModelFieldModification.m
%
% Purpose:
%     - Test functionality in removeFieldEntriesForType, extendModelFields,
%     getModelFieldsForType
%
% Author:
%     - Original file: Thomas Pfau

global CBTDIR

% save the current path
currentDir = pwd;

% initialize the test
fileDir = fileparts(which('testDynamicModelFieldModification.m'));
cd(fileDir);

%Lets create an empty model.
model = createModel();

%Lets add a gene:
model.genes{end+1,1} = 'gene1';

%extend all relevant fields (the original size was 0.
%get all Model fields for genes
fprintf('Testing getModelFieldsForType and extendModelFieldsForType ...\n');
[matchingGeneFields,dimensions] = getModelFieldsForType(model,'genes','fieldSize',0);
assert(dimensions == 2);
assert(isequal(matchingGeneFields,{'rxnGeneMat'}));
model = extendModelFieldsForType(model,'genes');
%This should result in an empty rxnGEneMatrix of size 0x1
assert(all(size(model.rxnGeneMat) == [0,1]));

%Recreate the model.
model = createModel();

%lets add a couple of reactions
reactions = {'R1',{'A','B'},[-1 1],0,1000;...
             'R2',{'B','C'},[-1 2],0,1000;...
             'R3',{'C','D'},[-1 1],0,1000;...
             'R4',{'E','F'},[-1 1],0,1000;...
             'R5',{'F','G'},[-1 1],0,1000;...
             'R6',{'G','E'},[-1 1],0,1000;...
             'R7',{'G','D'},[-1 1],0,1000;...
             'R8',{'H','C'},[-1 1],0,1000};
%Add Reactions
for i = 1:size(reactions,1)
    %All reactions are irreversible
    model = addReaction(model,reactions{i,1},'metaboliteList',reactions{i,2},...
          'stoichCoeffList',reactions{i,3},'lowerBound',reactions{i,4},'upperBound',reactions{i,5},...
          'printLevel',-1);
end
%Now, the size of model.mets is exactly the same as model.rxns (which could
%be a problematic situation).
%so, lets see, which fields we get for mets:
[matchingMetFields,dimensions] = getModelFieldsForType(model,'mets');
%This should be:
assert(isempty(setxor(matchingMetFields,{'mets','b','metNames','csense','S'})) && numel(matchingMetFields) == 5); %No duplicates!
%And S should have dimension 1, and only 1.
assert(all(dimensions(ismember(matchingMetFields,'S')) == 1)); %all to make sure that duplicate appearences are covered.

%Lets also check this for reactions
[matchingRxnFields,dimensions] = getModelFieldsForType(model,'rxns');
assert(isempty(setxor(matchingRxnFields,{'rxns','lb','ub','c','rxnGeneMat','rules','grRules','S'})) && numel(matchingRxnFields) == 8); %No duplicates!
%And S should have dimension 1, and only 1.
assert(all(dimensions(ismember(matchingRxnFields,'S')) == 2)); %all to make sure that duplicate appearences are covered.

%Finally for genes:
[matchingGeneFields,dimensions] = getModelFieldsForType(model,'genes');
assert(isempty(setxor(matchingGeneFields,{'genes','rxnGeneMat'})) && numel(matchingGeneFields) == 2); %No duplicates!
%And S should have dimension 1, and only 1.
assert(all(dimensions(ismember(matchingGeneFields,'rxnGeneMat')) == 2)); %all to make sure that duplicate appearences are covered.

%Now, add a metabolite
model.mets{end+1,1} = 'NewMet';
%and see, which fields we get when asking for all met fields one smaller
%than mets
[matchingMetFields,dimensions] = getModelFieldsForType(model,'mets','fieldSize',length(model.mets)-1);
%This should be all except mets:
assert(isempty(setxor(matchingMetFields,{'b','metNames','csense','S'})) && numel(matchingMetFields) == 4); %No duplicates!
%And S should have dimension 1, and only 1.
assert(all(dimensions(ismember(matchingMetFields,'S')) == 1)); %all to make sure that duplicate appearences are covered.

%now, lets add an unknown field
model.something = zeros(8,1); %This field will not be returned, as there are other fields (rxns) of the same size, so its unclear, what it belongs to.
[matchingMetFields,dimensions] = getModelFieldsForType(model,'mets','fieldSize',length(model.mets)-1);
assert(~any(ismember(matchingMetFields,'something')));

%If this would be size 9, it should be returned if all equal sized fields
%are requested:
model.something = zeros(1,9); %This field will not be returned, as there are other fields (rxns) of the same size, so its unclear, what it belongs to.
[matchingMetFields,dimensions] = getModelFieldsForType(model,'mets');
assert(any(ismember(matchingMetFields,'something')));
assert(dimensions(ismember(matchingMetFields,'something')) == 2);

% As a size 8 field it woudl be returned as a rxn field:
model.something = zeros(8,1); %This field will not be returned, as there are other fields (rxns) of the same size, so its unclear, what it belongs to.
[matchingRxnFields,dimensions] = getModelFieldsForType(model,'rxns');
assert(any(ismember(matchingRxnFields,'something')));
assert(dimensions(ismember(matchingRxnFields,'something')) == 1);

%So, lets extend the model to update all of those metabolite derived fields
model = extendModelFieldsForType(model,'mets');
[matchingMetFields,dimensions] = getModelFieldsForType(model,'mets');
assert(isempty(setxor(matchingMetFields,{'mets','b','metNames','csense','S'})) && numel(matchingMetFields) == 5);
assert(length(model.something) == 8) %This field was not modified, as it could not be clearly associated with mets.

fprintf('Testing removeFieldEntriesForType ...\n');
%Also, test the removeFieldEntries method now
%To be able to check whether the right fields are removed, we will create a
%random s matrix:
model.S = rand(size(model.S));
removedMets = [3 4 5 6];
model2 = removeFieldEntriesForType(model,removedMets,'mets', length(model.mets));
reducedSMatrix = model.S;
reducedSMatrix(removedMets,:) = [];
assert(isequal(model2.S, reducedSMatrix));
%Assert that the right mets are removed and that the others are retained.
assert(isempty(intersect(model2.mets,model.mets(removedMets))) && isempty(setxor(setdiff(model.mets,model.mets(removedMets)),model2.mets)))

%Now, remove a metabolite
model = removeMetabolites(model,model.mets{end});
% and try this again (same sized rxns and mets)
model2 = removeFieldEntriesForType(model,removedMets,'mets', length(model.mets));
reducedSMatrix = model.S;
reducedSMatrix(removedMets,:) = [];
assert(isequal(model2.S, reducedSMatrix));
%Assert that the right mets are removed and that the others are retained.
assert(isempty(intersect(model2.mets,model.mets(removedMets))) && isempty(setxor(setdiff(model.mets,model.mets(removedMets)),model2.mets)))

%Also give it a try with reactions:
% and try this again (same sized rxns and mets)
removedRxns = [3 4 5];
%Also modify lb, ub and rules for this case
model.lb = rand(size(model.rxns));
model.ub = rand(size(model.rxns));
model = changeGeneAssociation(model,model.rxns{1},'A or B');
model = changeGeneAssociation(model,model.rxns{3},'B and C');
model = changeGeneAssociation(model,model.rxns{6},'D or E');
model2 = removeFieldEntriesForType(model,removedRxns,'rxns', length(model.rxns));
[rxnPres,rxnPos] = ismember(model2.rxns,model.rxns);
assert(isequal(model2.S, model.S(:,rxnPos(rxnPres))));
%Assert that the right mets are removed and that the others are retained.
assert(isempty(intersect(model2.rxns,model.rxns(removedRxns))) && isempty(setxor(setdiff(model.rxns,model.rxns(removedRxns)),model2.rxns)))
assert(all(cellfun(@(x,y) isequal(x,y), model2.rules, model.rules(rxnPos(rxnPres)))));
assert(all(model2.lb == model.lb(rxnPos(rxnPres))));
assert(all(model2.ub == model.ub(rxnPos(rxnPres))));

%And finally give ti a try for genes:
%First create the GR rules.
model = creategrRulesField(model);
removedGenes = [2 3];
model2 = removeFieldEntriesForType(model,removedGenes,'genes', length(model.genes));
assert(isempty(model2.rules{3})); % Both genes got removed.
assert(isequal(model2.grRules{1},'A')); % B got removed, so only A is retained.
assert(isequal(model2.rules{6},'x(2) | x(3)')); % The gene positions got changed.

%And test remobval of gene 4
model2 = removeFieldEntriesForType(model,4,'genes', length(model.genes));
assert(isequal(model2.rules{6},'x(4)')); % 4 got removed, and 5 renamed
[e] = parseBoolean(model2.grRules{6});
[e2] = parseBoolean('E');
assert(isequal(e,e2));
%Compare irrespective of actual format.

%Switch back
cd(currentDir)


function test_pull769

% WALLTIME 00:10:00
% MEM 2gb
% DEPENDENCY ft_read_event

% the data was generated following the example at https://mne.tools/0.15/auto_tutorials/plot_creating_data_structures.html#tut-creating-data-structures
% and written to disk following https://mne.tools/0.15/auto_tutorials/plot_object_epochs.html
filename = dccnpath('/home/common/matlab/fieldtrip/data/test/pull769-epo.fif');

%%

hdr = ft_read_header(filename);
dat = ft_read_data(filename);
evt = ft_read_event(filename);

%%

cfg = [];
cfg.dataset = filename;
cfg.trialdef.eventvalue = 'smiling';
cfg = ft_definetrial(cfg);
data_smiling = ft_preprocessing(cfg);

assert(numel(data_smiling.time)==5);
assert(length(data_smiling.time{1})==200);

cfg = [];
cfg.dataset = filename;
cfg.trialdef.eventvalue = 'frowning';
cfg = ft_definetrial(cfg);
data_frowning = ft_preprocessing(cfg);

assert(numel(data_frowning.time)==5);
assert(length(data_frowning.time{1})==200);


function [origin,W] = db_get_origin_info(INorigin,dbname,varargin)

% ORIGIN = DB_GET_ORIGIN_INFO(EVENT,DATABASE_NAME) If EVENT is a vector
% of origin id numbers (orid) then the returned structure ORIGIN contains
% fields pulled from the origin table.
%
% If EVENT is a vector of string times (one time per row) then the database
% is searched for the origin occuring closest in time to each element of
% EVENT (preferred origins only). Using the ORID is a more precise way to
% pull events from the database, however, orid's have no inherent meaning
% and may change. Using (approximate) origin times avoids this.
%
% [ORIGIN,WAVEFORM] = DB_GET_ORIGIN_INFO(EVENT,DATABASE_NAME,WAVEFORM) Add
% origin information as new fields in WAVEFORM object.

% Author: Michael West, Geophysical Institute, Univ. of Alaska Fairbanks
% $Date$
% $Revision$

%  FOR TESTING ONLY
%dbname = '/home/admin/databases/AU06/wf/au06wf_2006_01';
%INorigin = [
%	'1/26/2006 11:58:59'
%	'1/27/2006 04:10:13'
%	'1/11/2006 07:01:49'
%	'1/31/2006 22:02:10'
%];
%INorigin = [
%	31301880
%	31301918
%	31300878
%	31302068
%];

if isnumeric(INorigin)
	origin.orid = reshape( INorigin , numel(INorigin), 1 );
elseif ischar(INorigin)
	for n = 1:size(INorigin,1)
		tmp(n) = str2epoch(INorigin(n,:));
	end
	tmp = getorids(tmp,dbname);
	origin.orid = reshape( tmp , numel(tmp), 1 );

else
    error('First input must be either origin ids or sting-formatted times');
end

% Check for waveform
if numel(varargin) == 1
	W = varargin{1};
	if ~isa(W,'waveform')
		error('Third argument must be a WAVEFORM object');
	end
elseif numel(varargin) > 1
	error('Too many arguments');
end
if nargout==2 && nargin~=3
	error('Mismatched number of input and/or output arguments');
end

% GET ORIGIN TIME
db = dbopen(dbname,'r');
db = dblookup_table(db,'origin');
nlist = [];
for n = 1:length(origin.orid);
	db1 = dbsubset(db,['orid==' num2str(origin.orid(n))]);
	[ origin.lat(n),origin.lon(n),origin.depth(n),origin.time_epoch(n),origin.ml(n),origin.orid(n)] = dbgetv(db1,'lat','lon','depth','time','ml','orid');
end
dbclose(db);
origin.lat = reshape( origin.lat , numel(origin.lat), 1 );
origin.lon = reshape( origin.lon , numel(origin.lon), 1 );
origin.depth = reshape( origin.depth , numel(origin.depth), 1 );
origin.time_epoch = reshape( origin.time_epoch , numel(origin.time_epoch), 1 );
origin.ml = reshape( origin.ml , numel(origin.ml), 1 );
origin.time_matlab = datenum(strtime(origin.time_epoch));
origin.orid = reshape( origin.orid , numel(origin.orid), 1 );
if exist('W')
	for n = 1:numel(W)
		disp([ 'Retreiving origin info for orid: ' num2str(origin.orid(n)) ' ... '])
		W(n) = addfield(W(n),'ORIGIN_LAT',origin.lat(n));
		W(n) = addfield(W(n),'ORIGIN_LON',origin.lon(n));
		W(n) = addfield(W(n),'ORIGIN_DEPTH',origin.depth(n));
		W(n) = addfield(W(n),'ORIGIN_TIME_EPOCH',origin.time_epoch(n));
		W(n) = addfield(W(n),'ORIGIN_ML',origin.ml(n));
		W(n) = addfield(W(n),'ORIGIN_TIME_MATLAB',origin.time_matlab(n));
		W(n) = addfield(W(n),'ORIGIN_ORID',origin.orid(n));
	end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% RETURN ORID FOR EVENT CLOSEST IN TIME TO INPUT
function neworigin = getorids(origin,dbname);

db = dbopen(dbname,'r');
db = dblookup_table(db,'origin');
db1 = dblookup_table(db,'event');
db = dbjoin(db,db1);
db = dbsubset(db,'orid==prefor');
nrecords = dbquery(db,'dbRECORD_COUNT');
%display(['Number of records: ' num2str(nrecords)]);
[orid,time] = dbgetv(db,'orid','time');
dbclose(db);

for n = 1:length(origin)
	[tmp,index] = min(abs(origin(n)-time));
	neworigin(n) = orid(index);
end;


classdef TestAllDemos < matlab.unittest.TestCase

    properties (TestParameter)
      demo = findDemos ();
    end

    methods (Test)
        function testDemo (testCase, demo)
            setup = 'pause off; warning off; ' ;
            try
                evalin ('base', [setup demo ' ; close all'])
                %evalc ([setup demo ' ; close all']);
            catch err
                testCase.verifyFail (err.message);
            end
        end
    end
end

function demos = findDemos ()

    vba_info = VBA_version();
    d = dir(fullfile(vba_info.path, '**', 'demo_*.m'));

    demos = {};
    for i = 1 : numel (d)
        demos{end+1} = d(i).name(1:end-2);
    end

end