classdef StarDetector < handle
    %STARDETECTOR  The class implements the Star keypoint detector
    %
    % ## References
    % [Agrawal08]:
    % > Motilal Agrawal, Kurt Konolige, and Morten Rufus Blas.
    % > "Censure: Center surround extremas for realtime feature detection and matching".
    % > In Computer Vision-ECCV 2008, pages 102-115. Springer, 2008.
    %
    % See also: cv.StarDetector.StarDetector, cv.FeatureDetector
    %

    properties (SetAccess = private)
        % Object ID
        id
    end

    methods
        function this = StarDetector(varargin)
            %STARDETECTOR  The full constructor
            %
            %     obj = cv.StarDetector()
            %     obj = cv.StarDetector(..., 'OptionName',optionValue, ...)
            %
            % ## Options
            % * __MaxSize__ maximum size of the features. The following values
            %   are supported: 4, 6, 8, 11, 12, 16, 22, 23, 32, 45, 46, 64,
            %   90, 128. In the case of a different value the result is
            %   undefined. default 45
            % * __ResponseThreshold__ threshold for the approximated laplacian,
            %   used to eliminate weak features. The larger it is, the less
            %   features will be retrieved. default 30
            % * __LineThresholdProjected__ another threshold for the laplacian
            %   to eliminate edges. default 10
            % * __LineThresholdBinarized__ yet another threshold for the
            %   feature size to eliminate edges. The larger the 2nd threshold,
            %   the more points you get. default 8
            % * __SuppressNonmaxSize__ default 5
            %
            % See also: cv.StarDetector.detect
            %
            this.id = StarDetector_(0, 'new', varargin{:});
        end

        function delete(this)
            %DELETE  Destructor
            %
            %     obj.delete()
            %
            % See also: cv.StarDetector
            %
            if isempty(this.id), return; end
            StarDetector_(this.id, 'delete');
        end

        function typename = typeid(this)
            %TYPEID  Name of the C++ type (RTTI)
            %
            %     typename = obj.typeid()
            %
            % ## Output
            % * __typename__ Name of C++ type
            %
            typename = StarDetector_(this.id, 'typeid');
        end
    end

    %% Algorithm
    methods
        function clear(this)
            %CLEAR  Clears the algorithm state
            %
            %     obj.clear()
            %
            % See also: cv.StarDetector.empty, cv.StarDetector.load
            %
            StarDetector_(this.id, 'clear');
        end

        function b = empty(this)
            %EMPTY  Checks if detector object is empty
            %
            %     b = obj.empty()
            %
            % ## Output
            % * __b__ Returns true if the detector object is empty (e.g in the
            %   very beginning or after unsuccessful read).
            %
            % See also: cv.StarDetector.clear
            %
            b = StarDetector_(this.id, 'empty');
        end

        function save(this, filename)
            %SAVE  Saves the algorithm parameters to a file
            %
            %     obj.save(filename)
            %
            % ## Input
            % * __filename__ Name of the file to save to.
            %
            % This method stores the algorithm parameters in the specified
            % XML or YAML file.
            %
            % See also: cv.StarDetector.load
            %
            StarDetector_(this.id, 'save', filename);
        end

        function load(this, fname_or_str, varargin)
            %LOAD  Loads algorithm from a file or a string
            %
            %     obj.load(fname)
            %     obj.load(str, 'FromString',true)
            %     obj.load(..., 'OptionName',optionValue, ...)
            %
            % ## Input
            % * __fname__ Name of the file to read.
            % * __str__ String containing the serialized model you want to
            %   load.
            %
            % ## Options
            % * __ObjName__ The optional name of the node to read (if empty,
            %   the first top-level node will be used). default empty
            % * __FromString__ Logical flag to indicate whether the input is a
            %   filename or a string containing the serialized model.
            %   default false
            %
            % This method reads algorithm parameters from the specified XML or
            % YAML file (either from disk or serialized string). The previous
            % algorithm state is discarded.
            %
            % See also: cv.StarDetector.save
            %
            StarDetector_(this.id, 'load', fname_or_str, varargin{:});
        end

        function name = getDefaultName(this)
            %GETDEFAULTNAME  Returns the algorithm string identifier
            %
            %     name = obj.getDefaultName()
            %
            % ## Output
            % * __name__ This string is used as top level XML/YML node tag
            %   when the object is saved to a file or string.
            %
            % See also: cv.StarDetector.save, cv.StarDetector.load
            %
            name = StarDetector_(this.id, 'getDefaultName');
        end
    end

    %% Features2D: FeatureDetector
    methods
        function keypoints = detect(this, img, varargin)
            %DETECT  Detects keypoints in an image or image set
            %
            %     keypoints = obj.detect(img)
            %     keypoints = obj.detect(imgs)
            %     [...] = obj.detect(..., 'OptionName',optionValue, ...)
            %
            % ## Input
            % * __img__ Image (first variant), 8-bit/16-bit grayscale image.
            % * __imgs__ Image set (second variant), cell array of images.
            %
            % ## Output
            % * __keypoints__ The detected keypoints. In the first variant, a
            %   1-by-N structure array. In the second variant of the method,
            %   `keypoints{i}` is a set of keypoints detected in `imgs{i}`.
            %
            % ## Options
            % * __Mask__ A mask specifying where to look for keypoints
            %   (optional). It must be a logical or 8-bit integer matrix with
            %   non-zero values in the region of interest. In the second
            %   variant, it is a cell-array of masks for each input image,
            %   `masks{i}` is a mask for `imgs{i}`. Not set by default.
            %
            % See also: cv.StarDetector.StarDetector
            %
            keypoints = StarDetector_(this.id, 'detect', img, varargin{:});
        end
    end

end


function varargout = sqdread(filename,varargin)
% [DATA,INFO] = SQDREAD(FILENAME,'CHANNELS',[C1 C2 C3,...],'SAMPLES',[S1 SN],'FORMAT','double');    % READ SQD-FILE
%
% SQDREAD Get Channel data and info saved in sqd file 'filename' using
% specifications from other options like 'Info','Samples', 'Channels',
% 'Format'. When the format is 'double', the MEG data has units of picotesla (pT).
% Usage:
% info = sqdread(filename,'Info');
%   where 'info' belongs to class sqdhandle. Use GET and SET methods to review or
%   change object properties. It contains experiment parameter values and
%   data acquisition parameters.
%
% [data,info] = sqdread(filename);
%   Get all the raw data saved in 'filename'. Data is arranged as following:
%	|Chan(0)Sample(1)   Chan(1)Sample(1)	... Chan(end-1)Sample(1)  |
%	|Chan(0)Sample(2)   Chan(1)Sample(2)	...	        .	        |
%	|   .			        .			                .	        |
%	|   .			        .			                .           |
%	|Chan(0)Sample(end) Chan(1)Sample(end)	... Chan(end-1)Sample(end)|
%
% [data,info] = sqdread(filename,'Channels',[0 1 2 3 4],'Samples',[2001 3000],...
%	'Format','int16');
%   Get data specified by Channels,Samples, and Format fields of inputs, ie
%   in the above example, data is returned for Channel numbers [0,1,2,3,4] and Samples
%   from 2001 to 3000. The data is returned as 'INT16'. Data will be arranged
%   as shown above in a increasing order. You may choose above options in any
%   order. If option is not specified, default values are assumed.
%   Default values for :
%       'Channels' - all available channels
%       'Samples'  - all available samples
%       'Format'   - 'double'
%
% Examples:
% If the path for the sqd-toolbox has been set properly and all of the files present
% (see README), there should be a sample raw sqd-file "testsqd.sqd" for testing. The
% sqd-file "testsqd.sqd" has data for 192 channels, 10 seconds of data and sampled
% at 1000Hz. The data is sinusoids of varying frequencies for the first 157 channels
% and random triggers for the rest. For the examples below, "testsqd.sqd" is used as
% the test sqd-file.
%
% 1. To read only the information regarding the data acquisition parameters:
%   a. info = sqdread('testsqd.sqd','info');
%       This will return you "info" which contains all the acquisition information.
%       Please make sure that when you specify the filename, it either includes the
%       filepath or you are in the directory where the file exists.
%   b. get(info);
%       This will tell you the parameters included in "info"
%   c. To get value of a parameter "fieldname" of "info".Choose fieldname
%      from parameters returned from get(info).
%      Ex.
%       info.PatientInfo        % this returns the PatientInfo = Matt Cheely,...
%       info.InputGain          % this returns the Amplifier InputGain = 2
%       info.SamplesAvailable   % this returns number of samples available per channel = 10000
%      In general, to get value for parameter "fieldname"
%       info.fieldname
%       get(info,'fieldname')
%
% 2. To read all the data:
%   data = sqdread('testsqd.sqd');
%       This will return you a 2-Dimensional matrix array "data" whose size will
%   be 10000 x 192 ie (Number of Samples x Number of Channels)
%
% 3. To read data and info together:
%   [data,info] = sqdread('testsqd.sqd');
%
% 4. To read in data for only specific channels and/or samples
%   a. To read in all the data for only channel N
%      N = 0;                                       % First channel
%      data = sqdread('testsqd.sqd','Channels',N);  % Read in the data
%      plot(data);                                  % To look at the signal
%      size(data)                                   % should be 10000 x 1
%                                                   % ie  (NumSamples x NumChannels)
%   b. To read in the data for all channels but only from Sample1 to SampleN
%      Sample1 = 1;                                 % First Sample
%      SampleN = 1000;                              % 1000th sample = 1second of data
%      data = sqdread('testsqd.sqd','Samples',[Sample1 SampleN]); % Read in the data
%      size(data)                                   % should be 1000 x 192
%                                                   % ie (NumSamples x NumChannels)
%   c. To read in data for channels N1,N2 and samples from Sample1 to SampleN
%      N1 = 0;                                      % First Channel
%      N2 = 99;                                     % 100th channel
%      Sample1 = 1;                                 % First Sample
%      SampleN = 1000;                              % 1000th sample = 1second of data
%      % Read in the data
%      data = sqdread('testsqd.sqd','Channels',[N1,N2],'Samples',[Sample1 SampleN]);
%      size(data)                                   % should be 1000 x 2
%                                                   % ie (NumSamples x NumChannels)
%
%
%   SEE ALSO,
%   SQDHANDLE,GETDATA,GET,SET

% This function is just a wrapper around the methods of CLASS SQDHANDLE
% It uses the filename to make a sqdhandle_obj and then calls its methods
% to return the information asked for. Very little error correction is done
% here. It is assumed that the methods of SQDHANDLE will take care of it.

% Get filename
% If cannot find file, ask user input
if nargin<1,
    filenameqn = input(...
        'Do you wish to choose an sqd-file to open? [Return,Yes|No]',...
        's');
    if isempty(filenameqn)|strncmpi(filenameqn,'y',1),
        [f,p] = uigetfile({'*.sqd;*.SQD'},'Get SQD-FILE');
        if ((~isstr(f)) | ~min(size(f))), return;end;  % If Load cancelled
        filename = [p f];
    else
        error('Valid sqd-filename required');
    end;
end;

if ischar(filename)
    info = sqdhandle(filename);                     % Get sqdhandle_obj from file
    switch nargin-1
        case 0                                      % Ex. data = sqdread(file.sqd);
            varargout{1} = getdata(info);           % Get data from sqdhandle_obj
            if nargout>=2                           % Ex. [data,info] = sqdread(file.sqd);
                varargout{2} = info;                % Return sqdhandle_obj as info
            end;
        case 1
            if strcmpi(varargin{1},'info')          % Ex. info = sqdread(file.sqd,'info');
                varargout{1} = info;                % Return sqdhandle_obj as info
            else
                error('Incorrect inputs')
            end;
        otherwise
            % Ex. data = sqdread(file.sqd,'Channels',[1 2],'Samples',[1 1000]);
            varargout{1} = getdata(info,varargin{:});
            if nargout>=2
                varargout{2} = info;
            end;
    end;
else
   error('First argument must be a valid filename');
end;


function hcell_node_display ( node_xy_file_name )

%*****************************************************************************80
%
%% HCELL_NODE_DISPLAY displays the nodes in the H-Cell.
%
%  Usage:
%
%    hcell_node_display ( node_xy_file_name )
%
%    A typical invocation might be
%
%      hcell_node_display ( 'xy3.txt' )
%
%    or
%
%      hcell_node_display ( 'xy6.txt' )
%
%    But if you simply say
%
%      hcell_node_display
%
%    the program will give you a chance to enter the file names
%    interactively.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    22 April 2004
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, filename NODE_XY_FILE_NAME, the name of a file
%    containing the XY coordinates of nodes.
%
  timestamp ( );
  fprintf ( 1, '\n' );
  fprintf ( 1, 'HCELL_NODE_DISPLAY:\n' );
  fprintf ( 1, '  MATLAB version\n' );
  fprintf ( 1, '\n' );
  fprintf ( 1, '  Display the nodes in the HCELL problem.\n' );
%
%  Do we have the XY file?
%
  if ( nargin < 1 )

    fprintf ( 1, '\n' );
    fprintf ( 1, 'HCELL_NODE_DISPLAY:\n' );
    node_xy_file_name = input ( 'Enter the name of the XY coordinate file:' );

  end

  [ m1, n1 ] = r8mat_header_read ( node_xy_file_name );

  node_xy = r8mat_data_read ( node_xy_file_name, m1, n1 );
%
%  Plot the nodes.
%
  plot ( node_xy(1,:), node_xy(2,:), 'bo' )
%
%  Add the boundary of the region to the plot.
%
  hcell_boundary_add ( 'r' );
%
%  Add the invisible bounding box.
%
  hcell_box_add ( 'w' )

  axis equal

  title ( 'Node coordinates' )

  text ( 45.0, 20.0, node_xy_file_name )

  fprintf ( 1, '\n' );
  fprintf ( 1, 'HCELL_NODE_DISPLAY:\n' );
  fprintf ( 1, '  Normal end of execution.\n' );
  fprintf ( 1, '\n' );
  timestamp ( );

  return
end
function column_num = file_column_count ( input_file_name )

%*****************************************************************************80
%
%% FILE_COLUMN_COUNT counts the columns in the first line of a file.
%
%  Discussion:
%
%    The file is assumed to be a simple text file.
%
%    Most lines of the file are presumed to consist of COLUMN_NUM words,
%    separated by spaces.  There may also be some blank lines, and some
%    comment lines, which have a "#" in column 1.
%
%    The routine tries to find the first non-comment non-blank line and
%    counts the number of words in that line.
%
%    If all lines are blanks or comments, it goes back and tries to analyze
%    a comment line.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    21 February 2004
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string INPUT_FILE_NAME, the name of the file.
%
%    Output, integer COLUMN_NUM, the number of columns in the file.
%
  FALSE = 0;
  TRUE = 1;
%
%  Open the file.
%
  input_unit = fopen ( input_file_name );

  if ( input_unit < 0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'FILE_COLUMN_COUNT - Error!\n' );
    fprintf ( 1, '  Could not open the file "%s".\n', input_file_name );
    error ( 'FILE_COLUMN_COUNT - Error!' );
    column_num = -1;
    return;
  end
%
%  Read one line, but skip blank lines and comment lines.
%  Use FGETL so we drop the newline character!
%
  got_one = FALSE;

  while ( 1 )

    line = fgetl ( input_unit );

    if ( line == -1 )
      break;
    end

    if ( s_len_trim ( line ) == 0 )

    elseif ( line(1) == '#' )

    else
      got_one = TRUE;
      break;
    end

  end

  fclose ( input_unit );

  if ( got_one == FALSE )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'FILE_COLUMN_COUNT - Warning!\n' );
    fprintf ( 1, '  The file does not seem to contain any data.\n' );
    column_num = -1;
    return;
  end

  column_num = s_word_count ( line );

  return
end
function row_num = file_row_count ( input_file_name )

%*****************************************************************************80
%
%% FILE_ROW_COUNT counts the number of row records in a file.
%
%  Discussion:
%
%    Each input line is a "RECORD".
%
%    The records are divided into three groups:
%
%    * BLANK LINES (nothing but blanks)
%    * COMMENT LINES (begin with a '#')
%    * DATA RECORDS (anything else)
%
%    The value returned by the function is the number of data records.
%
%    By the way, if the MATLAB routine FGETS is used, instead of
%    FGETL, then the variable LINE will include line termination
%    characters, which means that a blank line would not actually
%    have zero characters.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    31 December 2006
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string INPUT_FILE_NAME, the name of the input file.
%
%    Output, integer ROW_NUM, the number of rows found.
%
  input_unit = fopen ( input_file_name );

  if ( input_unit < 0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'FILE_ROW_COUNT - Error!\n' );
    fprintf ( 1, '  Could not open the file "%s".\n', input_file_name );
    error ( 'FILE_ROW_COUNT - Error!' );
    row_num = -1;
    return;
  end

  blank_num = 0;
  comment_num = 0;
  row_num = 0;

  record_num = 0;

  while ( 1 )

    line = fgetl ( input_unit );

    if ( line == -1 )
      break;
    end

    record_num = record_num + 1;
    record_length = s_len_trim ( line );

    if ( record_length <= 0 )
      blank_num = blank_num + 1;
    elseif ( line(1) == '#' )
      comment_num = comment_num + 1;
    else
      row_num = row_num + 1;
    end

  end

  fclose ( input_unit );

  return
end
function hcell_box_add ( box_color )

%*****************************************************************************80
%
%% HCELL_BOX_ADD adds a box around the HCELL region.
%
%  Discussion:
%
%    This routine can only ADD graphics to an already existing plot.
%    So call PLOT first and then this routine.
%
%    The box is normally drawn with the color white ('w').
%    In this case, it doesn't actually appear on the screen.
%    However, it does force MATLAB to make the picture bigger,
%    giving us space to write some titles.
%
%  Usage:
%
%    hcell_box_add ( box_color )
%
%    A typical invocation might be
%
%      hcell_box_add ( 'w' )
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    23 April 2004
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, character BOX_COLOR, the color to be used to draw the box.
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'HCELL_BOX_ADD:\n' );
  fprintf ( 1, '  Add a bounding box to a plot of the HCELL problem.\n' );
%
%  Do we have a color?
%
  if ( nargin < 1 )
    box_color = 'w'
  end
%
%  Set the coordinates of the invisible bounding box that
%  allows us to display the file name within the plot area.
%
  box_x = [ -5.00,  110.00, 110.00, -5.00, -5.00 ];
  box_y = [ -5.00,   -5.00, 21.00,  21.00, -5.00 ];

  line ( box_x, box_y, 'color', box_color )

  fprintf ( 1, '\n' );
  fprintf ( 1, 'HCELL_BOX_ADD:\n' );
  fprintf ( 1, '  Normal end of execution.\n' );

  return
end
function table = r8mat_data_read ( input_filename, m, n )

%*****************************************************************************80
%
%% R8MAT_DATA_READ reads data from an R8MAT file.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    27 January 2006
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string INPUT_FILENAME, the name of the input file.
%
%    Input, integer M, N, the number of rows and columns of data.
%
%    Output, real TABLE(M,N), the point coordinates.
%
  table = [];
%
%  Build up the format string for reading M real numbers.
%
  string = ' ';

  for i = 0 : m
    string = strcat ( string, ' %f' );
  end

  input_unit = fopen ( input_filename );

  if ( input_unit < 0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'R8MAT_DATA_READ - Error!\n' );
    fprintf ( 1, '  Could not open the file.\n' );
    error ( 'R8MAT_DATA_READ - Error!' );
    return;
  end

  i = 0;

  while ( i < n )

    line = fgets ( input_unit );

    if ( line == -1 )
      break;
    end

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

    elseif ( s_len_trim ( line ) == 0 )

    else

      [ x, count ] = sscanf ( line, string );

      if ( count == m )
        i = i + 1;
        table(1:m,i) = x(1:m);
      end

    end

  end

  fclose ( input_unit );

  return
end
function [ m, n ] = r8mat_header_read ( input_filename )

%*****************************************************************************80
%
%% R8MAT_HEADER_READ reads the header from an R8MAT file.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    22 October 2004
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string INPUT_FILENAME, the name of the input file.
%
%    Output, integer M, the spatial dimension.
%
%    Output, integer N, the number of points.
%
  m = file_column_count ( input_filename );

  if ( m <= 0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'R8MAT_HEADER_READ - Fatal error!\n' );
    fprintf ( 1, '  There was some kind of I/O problem while trying\n' );
    fprintf ( 1, '  to count the number of data columns in\n' );
    fprintf ( 1, '  the file %s.\n', input_filename );
  end

  n = file_row_count ( input_filename );

  if ( n <= 0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'R8MAT_HEADER_READ - Fatal error!\n' );
    fprintf ( 1, '  There was some kind of I/O problem while trying\n' );
    fprintf ( 1, '  to count the number of data rows in\n' );
    fprintf ( 1, '  the file %s\n', input_filename );
  end

  return
end
function timestamp ( )

%*****************************************************************************80
%
%% TIMESTAMP prints the current YMDHMS date as a timestamp.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    14 February 2003
%
%  Author:
%
%    John Burkardt
%
  t = now;
  c = datevec ( t );
  s = datestr ( c, 0 );
  fprintf ( 1, '%s\n', s );

  return
end


function varargout = subsref(F, index)
%SUBSREF   BALLFUNV subsref.
%
% ( )
%   F(X,Y,Z) returns the values of the BALLFUNV F evaluated on the array
%   (X,Y,Z) in cartesian coordinates.
%
%   F(R, L, TH, 'spherical') returns the values of the BALLFUNV F evaluated
%   on the array (R,TH,LAM) in spherical coordinates.
%
%   F(K) returns the first component of F if K = 1, the second if K = 2, and
%   the third if K = 3.
%
%   F(R, :, :, 'spherical') returns a SPHEREFUNV representing the BALLFUNV
%   F along a radial shell.
%
%   F(:, :, :) returns F.
%
%  .
%   F.PROP returns the property PROP of F as defined by GET(F,'PROP').
%
% { }
%    Throws an error.

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

% check for empty CHEBFUN2V object.
if ( isempty(F) )
    varargout = {[]};
    return
end

% Recursive going through the ref structure:
idx = index(1).subs;
switch index(1).type

    case '.'
        % Call GET() for .PROP access.
        out = get(F, idx);
        if ( numel(index) > 1 )
            % Recurse on SUBSREF():
            index(1) = [];
            out = subsref(out, index);
        end
        varargout = {out};

    case '()'
        if ( isa(idx{1}, 'double') && numel(idx)==1 )
            Fc = F.comp;
                if all( idx{1} == 1  )
                    varargout = {Fc{1}};
                elseif ( all( idx{1} == 2 ) )
                    varargout = {Fc{2}};
                elseif ( ( all(idx{1} == 3) ) )
                    varargout = {Fc{3}};
                else
                    error('CHEBFUN:BALLFUNV:subsref:index', ...
                        'BALLFUNV only contains three components');
                end
        else
            % F(X,Y,Z), F(X,Y,Z,str), etc.
           Fc = F.comp;
           v1 = subsref(Fc{1}, index);
           v2 = subsref(Fc{2}, index);
           v3 = subsref(Fc{3}, index);
           varargout = {[v1 ; v2 ; v3]};
        end

    otherwise
        error('CHEBFUN:BALLFUNV:subsref:unexpectedType', ...
            ['??? Unexpected index.type of ' index(1).type]);

end

% Recurse down:
if ( numel(index) > 1 )
    index(1) = [];
    varargout = { subsref( varargout{ : }, index ) };
end

end


% ------------------------------------------------------------------------
%  Copyright (C)
%  Universitat Politecnica de Catalunya BarcelonaTech (UPC) - Spain
%  University of California Berkeley (UCB) - USA
%
%  Jordi Pont-Tuset <jordi.pont@upc.edu>
%  Pablo Arbelaez <arbelaez@berkeley.edu>
%  June 2014
% ------------------------------------------------------------------------
% This file is part of the MCG package presented in:
%    Arbelaez P, Pont-Tuset J, Barron J, Marques F, Malik J,
%    "Multiscale Combinatorial Grouping,"
%    Computer Vision and Pattern Recognition (CVPR) 2014.
% Please consider citing the paper if you use this code.
% ------------------------------------------------------------------------
%
% function masks = cands2labels(candidates,ms)
%
% From a hierarchy (lp: leaves partition and ms: merging sequence) and a
% list of candidates as sets of labels (candidates), we get each candidate
% as a list of labels of the leaves partition lp.
% ------------------------------------------------------------------------
function labels = cands2labels(candidates,ms)
    labels = mex_cands2labels(double(ms),candidates);
end


%% Example
% An example script, it is used to show how to use the different
% functions of the KUKA iiwa matlab toolbox

% First run the following script in Matlab
% Then start the client on the KUKA iiwa controller

% Mohammad SAFEEA, 24th of April 2018

close all,clear all,clc
warning('off');
ip='172.31.1.147'; % The IP of the controller
% start a connection with the server
t_Kuka=net_establishConnection( ip );

if ~exist('t_Kuka','var') || isempty(t_Kuka) || strcmp(t_Kuka.Status,'closed')
  warning('Connection could not be establised, script aborted');
  return;
else

    %% move to initial position
pinit={0,pi*20/180,0,-pi*70/180,0,pi*90/180,0}; % initial confuguration
relVel=0.15; % relative velocity
movePTPJointSpace( t_Kuka , pinit, relVel); % point to point motion in joint space

      %% Get position roientation of end effector
      disp('Cartesian position')
      Pos=getEEFPos( t_Kuka )

      relVel=50; % velocity of the end effector, mm/sec

      disp=50;
      %% Move the endeffector in the X direction
      index=1;
      Pos{index}=Pos{index}+disp;

       movePTPLineEEF( t_Kuka , Pos, relVel)
      pause(0.1)
      Pos{index}=Pos{index}-disp;

      movePTPLineEEF( t_Kuka , Pos, relVel)

      %% Move the endeffector in the z direction

       index=3;
      Pos{index}=Pos{index}+disp;

       movePTPLineEEF( t_Kuka , Pos, relVel)
       pause(0.1)
       Pos{index}=Pos{index}-disp;

      movePTPLineEEF( t_Kuka , Pos, relVel)

      %% Move the endeffector to a distination frame,

       Pos={400,0,580,-pi,0,-pi}; % first configuration
       % the coordinates are:
       % x=400mm, y=0mm, z=580 mm.
       % the rotation angles are:
       % alpha=-180 degrees, beta=0 degrees, gama=-180 degrees

       movePTPLineEEF( t_Kuka , Pos, relVel)

       Pos={500,0,580,-pi,0,-pi+pi/4}; %% second configuration
       % the coordinates are:
       % x=400mm, y=0mm, z=580 mm.
       % the rotation angles are:
       % alpha=-180 degrees, beta=0 degrees, gama=-165 degrees,
       setBlueOn(t_Kuka);
       movePTPLineEEF( t_Kuka , Pos, relVel)
       setBlueOff(t_Kuka);
      %% turn off the server
       net_turnOffServer( t_Kuka );

       fclose(t_Kuka);
end
warning('on');


function [ ] = save_callback_denoising_general( theta, info, state, eI, varargin)
% Copyright (c) 2014-present University of Illinois at Urbana-Champaign
% All rights reserved.
%
% Developed by:     Po-Sen Huang, Paris Smaragdis
%                   Department of Electrical and Computer Engineering
%                   Department of Computer Science
%
% save model and run evaluation while minfunc is running

if mod(info.iteration, 100) == 0
    if isfield(eI, 'iterStart')
      info.iteration = info.iteration+eI.iterStart;
    end
    if ~strcmp(state,'init')
        if isfield(eI,'ioffset'),
            saveName = sprintf('%smodel_off%d_%d.mat',eI.saveDir,eI.ioffset, info.iteration);
        else
            saveName = sprintf('%smodel_%d.mat',eI.saveDir,info.iteration);
        end
        save(saveName, 'theta', 'eI',  'info');
    end
    % run evaluation
%       if mod(info.iteration, 20) == 0 && info.iteration>=0
%          test_mir1k_general_kl_bss3(eI.modelname, theta, eI, 'iter', info.iteration);
%       end
end;

end


options.P = 3;
filters = morlet_filter_bank_2d_pyramid(options);


function demtrain(action);
%DEMTRAIN Demonstrate training of MLP network.
%
%	Description
%	DEMTRAIN brings up a simple GUI to show the training of an MLP
%	network on classification and regression problems.  The user should
%	load in a dataset (which should be in Netlab format: see  DATREAD),
%	select the output activation function, the  number of cycles and
%	hidden units and then train the network. The scaled conjugate
%	gradient algorithm is used. A graph shows the evolution of the error:
%	the value is shown  MAX(CEIL(ITERATIONS / 50), 5) cycles.
%
%	Once the network is trained, it is saved to the file MLPTRAIN.NET.
%	The results can then be viewed as a confusion matrix (for
%	classification problems) or a plot of output versus target (for
%	regression problems).
%
%	See also
%	CONFMAT, DATREAD, MLP, NETOPT, SCG
%

%	Copyright (c) Ian T Nabney (1996-2001)

% If run without parameters, initialise gui.
if nargin<1,
  action='initialise';
end;

% Global variable to reference GUI figure
global DEMTRAIN_FIG
% Global array to reference sub-figures for results plots
global DEMTRAIN_RES_FIGS
global NUM_DEMTRAIN_RES_FIGS

if strcmp(action,'initialise'),

  file = '';
  path = '.';

  % Create FIGURE
  fig = figure( ...
	'Name', 'Netlab Demo', ...
	'NumberTitle', 'off', ...
	'Menubar', 'none', ...
	'Color', [0.7529 0.7529 0.7529], ...
	'Visible', 'on');
  % Initialise the globals
  DEMTRAIN_FIG = fig;
  DEMTRAIN_RES_FIGS = 0;
  NUM_DEMTRAIN_RES_FIGS = 0;

  % Create GROUP for buttons
  uicontrol(fig, ...
	'Style', 'frame', ...
	'Units', 'normalized', ...
	'Position', [0.03 0.08 0.94 0.22], ...
	'BackgroundColor', [0.5 0.5 0.5]);

  % Create MAIN axis
  hMain = axes( ...
	'Units', 'normalized', ...
	'Position', [0.10 0.5 0.80 0.40], ...
	'XColor', [0 0 0], ...
	'YColor', [0 0 0], ...
	'Visible', 'on');

  % Create static text for FILENAME and PATH
  hFilename = uicontrol(fig, ...
	'Style', 'text', ...
	'Units', 'normalized', ...
	'BackgroundColor', [0.7529 0.7529 0.7529], ...
	'Position', [0.05 0.32 0.90 0.05], ...
	'HorizontalAlignment', 'center', ...
	'String', 'Please load data file.', ...
	'Visible', 'on');
  hPath = uicontrol(fig, ...
	'Style', 'text', ...
	'Units', 'normalized', ...
	'BackgroundColor', [0.7529 0.7529 0.7529], ...
	'Position', [0.05 0.37 0.90 0.05], ...
	'HorizontalAlignment', 'center', ...
	'String', '', ...
	'Visible', 'on');

  % Create NO OF HIDDEN UNITS slider and text
  hSliderText = uicontrol(fig, ...
	'Style', 'text', ...
	'BackgroundColor', [0.5 0.5 0.5], ...
	'Units', 'normalized', ...
	'Position', [0.27 0.12 0.17 0.04], ...
	'HorizontalAlignment', 'right', ...
	'String', 'Hidden Units: 5');
  hSlider = uicontrol(fig, ...
	'Style', 'slider', ...
	'Units', 'normalized', ...
	'Position', [0.45 0.12 0.26 0.04], ...
	'String', 'Slider', ...
	'Min', 1, 'Max', 25, ...
	'Value', 5, ...
	'Callback', 'demtrain slider_moved');

  % Create ITERATIONS slider and text
  hIterationsText = uicontrol(fig, ...
	'Style', 'text', ...
	'BackgroundColor', [0.5 0.5 0.5], ...
	'Units', 'normalized', ...
	'Position', [0.27 0.21 0.17 0.04], ...
	'HorizontalAlignment', 'right', ...
	'String', 'Iterations: 50');
  hIterations = uicontrol(fig, ...
	'Style', 'slider', ...
	'Units', 'normalized', ...
	'Position', [0.45 0.21 0.26 0.04], ...
	'String', 'Slider', ...
	'Min', 10, 'Max', 500, ...
	'Value', 50, ...
	'Callback', 'demtrain iterations_moved');

  % Create ACTIVATION FUNCTION popup and text
  uicontrol(fig, ...
	'Style', 'text', ...
	'BackgroundColor', [0.5 0.5 0.5], ...
	'Units', 'normalized', ...
	'Position', [0.05 0.20 0.20 0.04], ...
	'HorizontalAlignment', 'center', ...
	'String', 'Activation Function:');
  hPopup = uicontrol(fig, ...
	'Style', 'popup', ...
	'Units', 'normalized', ...
	'Position' , [0.05 0.10 0.20 0.08], ...
	'String', 'Linear|Logistic|Softmax', ...
	'Callback', '');

  % Create MENU
  hMenu1 = uimenu('Label', 'Load Data file...', 'Callback', '');
  uimenu(hMenu1, 'Label', 'Select training data file', ...
	'Callback', 'demtrain get_ip_file');
  hMenu2 = uimenu('Label', 'Show Results...', 'Callback', '');
  uimenu(hMenu2, 'Label', 'Show classification results', ...
	'Callback', 'demtrain classify');
  uimenu(hMenu2, 'Label', 'Show regression results', ...
	'Callback', 'demtrain predict');

  % Create START button
  hStart = uicontrol(fig, ...
	'Units', 'normalized', ...
	'Position' , [0.75 0.2 0.20 0.08], ...
	'String', 'Start Training', ...
	'Enable', 'off',...
	'Callback', 'demtrain start');

  % Create CLOSE button
  uicontrol(fig, ...
	'Units', 'normalized', ...
	'Position' , [0.75 0.1 0.20 0.08], ...
	'String', 'Close', ...
	'Callback', 'demtrain close');

  % Save handles of important UI objects
  hndlList = [hSlider hSliderText hFilename hPath hPopup ...
      hIterations hIterationsText hStart];
  set(fig, 'UserData', hndlList);
  % Hide window from command line
  set(fig, 'HandleVisibility', 'callback');

elseif strcmp(action, 'slider_moved'),

  % Slider has been moved.

  hndlList = get(gcf, 'UserData');
  hSlider = hndlList(1);
  hSliderText = hndlList(2);

  val = get(hSlider, 'Value');
  if rem(val, 1) < 0.5,  % Force up and down arrows to work!
	val = ceil(val);
  else
	val = floor(val);
  end;
  set(hSlider, 'Value', val);
  set(hSliderText, 'String', ['Hidden Units: ' int2str(val)]);

elseif strcmp(action, 'iterations_moved'),

  % Slider has been moved.

  hndlList = get(gcf, 'UserData');
  hSlider = hndlList(6);
  hSliderText = hndlList(7);

  val = get(hSlider, 'Value');
  set(hSliderText, 'String', ['Iterations: ' int2str(val)]);

elseif strcmp(action, 'get_ip_file'),

  % Get data file button pressed.

  hndlList = get(gcf, 'UserData');

  [file, path] = uigetfile('*.dat', 'Get Data File', 50, 50);

  if strcmp(file, '') | file == 0,
    set(hndlList(3), 'String', 'No data file loaded.');
    set(hndlList(4), 'String', '');
  else
    set(hndlList(3), 'String', file);
    set(hndlList(4), 'String', path);
  end;

  % Enable training button
  set(hndlList(8), 'Enable', 'on');

  set(gcf, 'UserData', hndlList);

elseif strcmp(action, 'start'),

  % Start training

  % Get handles of and values from UI objects
  hndlList = get(gcf, 'UserData');
  hSlider = hndlList(1); % 				No of hidden units
  hIterations = hndlList(6);
  iterations = get(hIterations, 'Value');

  hFilename = hndlList(3);	% 			Data file name
  filename = get(hFilename, 'String');

  hPath = hndlList(4);	% 				Data file path
  path = get(hPath, 'String');

  hPopup = hndlList(5);		% 			Activation function
  if get(hPopup, 'Value') == 1,
	act_fn = 'linear';
  elseif get(hPopup, 'Value') == 2,
	act_fn = 'logistic';
  else
	act_fn = 'softmax';
  end;
  nhidden = get(hSlider, 'Value');

  % Check data file exists
  if fopen([path '/' filename]) == -1,
	errordlg('Training data file has not been selected.', 'Error');
  else
	% Load data file
	[x,t,nin,nout,ndata] = datread([path filename]);

	% Call MLPTRAIN function repeatedly, while drawing training graph.
	figure(DEMTRAIN_FIG);
	hold on;

	title('Training - please wait.');

	% Create net and find initial error
	net = mlp(size(x, 2), nhidden, size(t, 2), act_fn);
	% Initialise network with inverse variance of 10
	net = mlpinit(net, 10);
	error = mlperr(net, x, t);
	% Work out reporting step: should be sufficiently big to let training
	% algorithm have a chance
	step = max(ceil(iterations / 50), 5);

	% Refresh and rescale axis.
	cla;
	max = error;
	min = max/10;
	set(gca, 'YScale', 'log');
	ylabel('log Error');
	xlabel('No. iterations');
	axis([0 iterations min max+1]);
	iold = 0;
	errold = error;
	% Plot circle to show error of last iteration
	% Setting erase mode to none prevents screen flashing during
	% training
	plot(0, error, 'ro', 'EraseMode', 'none');
	hold on
	drawnow; % Force redraw
	for i = step-1:step:iterations,
	  [net, error] = mlptrain(net, x, t, step);
	  % Plot line from last point to new point.
	  line([iold i], [errold error], 'Color', 'r', 'EraseMode', 'none');
	  iold = i;
	  errold = error;

	  % If new point off scale, redraw axes.
	  if error > max,
	    max = error;
	    axis([0 iterations min max+1]);
	  end;
	  if error < min
	    min = error/10;
	    axis([0 iterations min max+1]);
	  end
	  % Plot circle to show error of last iteration
	  plot(i, error, 'ro', 'EraseMode', 'none');
	  drawnow; % Force redraw
	end;
	save mlptrain.net net
	zoom on;

	title(['Training complete. Final error=', num2str(error)]);

  end;

elseif strcmp(action, 'close'),

  % Close all the figures we have created
  close(DEMTRAIN_FIG);
  for n = 1:NUM_DEMTRAIN_RES_FIGS
    if ishandle(DEMTRAIN_RES_FIGS(n))
      close(DEMTRAIN_RES_FIGS(n));
    end
  end

elseif strcmp(action, 'classify'),

  if fopen('mlptrain.net') == -1,
	errordlg('You have not yet trained the network.', 'Error');
  else

	hndlList = get(gcf, 'UserData');
	filename = get(hndlList(3), 'String');
	path = get(hndlList(4), 'String');
	[x,t,nin,nout,ndata] = datread([path filename]);
	load mlptrain.net net -mat
	y = mlpfwd(net, x);

	% Save results figure so that it can be closed later
	NUM_DEMTRAIN_RES_FIGS = NUM_DEMTRAIN_RES_FIGS + 1;
	DEMTRAIN_RES_FIGS(NUM_DEMTRAIN_RES_FIGS)=conffig(y,t);

  end;

elseif strcmp(action, 'predict'),

  if fopen('mlptrain.net') == -1,
	errordlg('You have not yet trained the network.', 'Error');
  else

	hndlList = get(gcf, 'UserData');
	filename = get(hndlList(3), 'String');
	path = get(hndlList(4), 'String');
	[x,t,nin,nout,ndata] = datread([path filename]);
	load mlptrain.net net -mat
	y = mlpfwd(net, x);

	for i = 1:size(y,2),
	  % Save results figure so that it can be closed later
	  NUM_DEMTRAIN_RES_FIGS = NUM_DEMTRAIN_RES_FIGS + 1;
	  DEMTRAIN_RES_FIGS(NUM_DEMTRAIN_RES_FIGS) = figure;
	  hold on;
	  title(['Output no ' num2str(i)]);
	  plot([0 1], [0 1], 'r:');
	  plot(y(:,i),t(:,i), 'o');
	  hold off;
	end;
  end;

end;


function [f,handles] = TS_Plot2d(featureData,TimeSeries,featureLabels,annotateParams,...
                                            showDistr,cfnParams)
% TS_Plot2d   Plot a dataset in a two-dimensional space.
%
% e.g., The space of two chosen features, or two principal components.
%
%---INPUTS:
% featureData, an Nx2 vector of where to plot each of the N data objects in the
%           two-dimensional space
%
% TimeSeries, table of time-series metadata
%
% featureLabels, cell of labels for each feature
%
% annotateParams, a structure containing all the information about how to annotate
%           data points. Fields can include (cf. BF_AnnotatePoints):
%               - n, the number of data points to annotate
%               - userInput, 0: randomly selected datapoints, 1: user clicks to annotate datapoints
%               - fdim, 1x2 vector with width and height of time series as fraction of plot size
%               - maxL, maximum length of annotated time series
%               - textAnnotation: 'Name', 'ID', or 'none' to annotate this data
%               - cmap, a cell of colors, with elements for each group
%               - theMarkerSize, a custom marker size
%               - theLineWidth: line width for annotated time series
%
% showDistr, if true (default), plots marginal density estimates for each variable
%                   (above and to the right of the plot), otherwise set to false.
%
% cfnParams, (optional) custom classification parameters to fit.

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

% ------------------------------------------------------------------------------
%% Check Inputs:
% ------------------------------------------------------------------------------

% featureData should be a Nx2 vector of where to plot each of the N data objects
% in the two-dimensional space:
if nargin < 1
    error('You must provide two-dimensional feature vectors for the data.')
end

if nargin < 3 || isempty(featureLabels)
    featureLabels = {'',''};
end

if nargin < 4 || isempty(annotateParams)
    annotateParams = struct('n',6); % annotate for six points
end

% By default, plot kernel density estimates above and on the side of the plot:
if nargin < 5 || isempty(showDistr)
    showDistr = true;
end

if nargin < 6
    cfnParams = GiveMeDefaultClassificationParams(TimeSeries);
end
if isfield(cfnParams,'numFolds')
    cfnParams.numFolds = 0;
end

if isfield(annotateParams,'makeFigure')
    makeFigure = annotateParams.makeFigure;
else
    makeFigure = true; % default is to plot on a brand new figure('color','w')
end

%-------------------------------------------------------------------------------
% Preliminaries
%-------------------------------------------------------------------------------
groupLabels = TS_ExtractGroupLabels(TimeSeries);
classLabels = categories(groupLabels);
numClasses = length(classLabels);

%-------------------------------------------------------------------------------
%% Plot
%-------------------------------------------------------------------------------
if makeFigure % can set extras.makeFigure = 0 to plot within a given setting
    f = figure('color','w');
    box('on'); % white figure
    f.Position(3:4) = [600, 550];
else
    f = gcf;
end

% Set colors
if (numClasses==0) || (numClasses==1)
    groupColors = {[0,0,0]}; % Just use black...
else
    if isstruct(annotateParams) && isfield(annotateParams,'cmap')
        if iscell(annotateParams.cmap)
            groupColors = annotateParams.cmap; % specify the cell itself
        elseif ischar(annotateParams.cmap)
            groupColors = BF_GetColorMap(annotateParams.cmap,numClasses,1);
        end
    else
        groupColors = GiveMeColors(numClasses);
    end
end
annotateParams.groupColors = groupColors;

% ------------------------------------------------------------------------------
%% Plot distributions
% ------------------------------------------------------------------------------
if showDistr
    % Top distribution (marginal of first feature)
    subplot(4,4,1:3); hold('on'); box('on')
    maxx = 0; minn = 100;
    for i = 1:numClasses
        fr = BF_plot_ks(featureData(groupLabels==classLabels{i},1),groupColors{i},0);
        maxx = max([maxx,fr]); minn = min([minn,fr]);
    end
    axTop = gca;
    set(axTop,'XTickLabel',[]);
    set(axTop,'YTickLabel',[]);
    set(axTop,'ylim',[minn,maxx]);

    % Side distribution (marginal of second feature)
    subplot(4,4,[8,12,16]); hold on; box('on')
    maxx = 0; minn = 100;
    for i = 1:numClasses
        fr = BF_plot_ks(featureData(groupLabels==classLabels{i},2),groupColors{i},1);
        maxx = max([maxx,fr]); minn = min([minn,fr]);
    end
    axSide = gca;
    set(axSide,'XTickLabel',[]);
    set(axSide,'YTickLabel',[]);
    set(axSide,'xlim',[minn,maxx]);
end

% ------------------------------------------------------------------------------
%% Set up a 2D plot
% ------------------------------------------------------------------------------
if showDistr
    subplot(4,4,[5:7,9:11,13:15]); box('on');
    axMain = gca;
end
hold('on');

if isfield(annotateParams,'theMarkerSize');
    theMarkerSize = annotateParams.theMarkerSize; % specify custom marker size
else
    theMarkerSize = 12; % Marker size for '.'
end

handles = cell(numClasses,1);
for i = 1:numClasses
    handles{i} = plot(featureData(groupLabels==classLabels{i},1),...
                featureData(groupLabels==classLabels{i},2),...
                '.','color',groupColors{i},'MarkerSize',theMarkerSize);
end

% Link axes
if showDistr
    linkaxes([axMain,axTop],'x');
    linkaxes([axMain,axSide],'y');
end

%-------------------------------------------------------------------------------
% Annotate points:
%-------------------------------------------------------------------------------
% Label axes first without classification rates so the user can see what they're doing when annotating
labelAxes(0);
if ismember('Data',TimeSeries.Properties.VariableNames)
    % Annotate if time-series data is provided

    % Produce xy points
    xy = featureData;

    % Go-go-go:
    BF_AnnotatePoints(xy,TimeSeries,annotateParams);
end

% ------------------------------------------------------------------------------
%% Do classification and plot a classify boundary?
% ------------------------------------------------------------------------------
didClassify = false;
if numClasses > 1 && ~isempty(fields(cfnParams))
    % Compute the in-sample classification rate:
    classRate = nan(3,1); % classRate1, classRate2, classRateboth
    try
        fprintf(1,'Estimating %u-class classification rates for each feature (and in combination)...\n',numClasses);
        classRate(1) = GiveMeCfn(featureData(:,1),groupLabels,featureData(:,1),groupLabels,cfnParams);
        classRate(2) = GiveMeCfn(featureData(:,2),groupLabels,featureData(:,2),groupLabels,cfnParams);
        classRate(3) = GiveMeCfn(featureData,groupLabels,featureData(:,1:2),groupLabels,cfnParams);
        % Record that classification was performed successfully:
        didClassify = true;
        fprintf(1,'%s in 2-dim space: %.2f%%\n',cfnParams.whatLoss,classRate(3));
    catch emsg
        fprintf(1,'\nClassification rates not computed\n(%s)\n',emsg.message);
    end

    % Also plot an SVM classification boundary:
    if numClasses < 5
        fprintf(1,'Estimating classification boundaries...\n');
        % Train the model (in-sample):
        [~,Mdl] = GiveMeCfn(featureData,groupLabels,featureData,groupLabels,cfnParams);

        % Predict scores over the 150x150 grid through space
        gridInc = 150;
        [x1Grid,x2Grid] = meshgrid(linspace(min(featureData(:,1)),max(featureData(:,1)),gridInc),...
                                   linspace(min(featureData(:,2)),max(featureData(:,2)),gridInc));
        fullGrid = [x1Grid(:),x2Grid(:)];
        predLabels = predict(Mdl,fullGrid);

        % For each class plot the contour of their region of the space:
        if numClasses==2
            integerPredLabels = arrayfun(@(x)find(classLabels==x),predLabels);
            contour(x1Grid,x2Grid,reshape(integerPredLabels-1.5,size(x1Grid)),[0 0],...
                                    '--k','LineWidth',2);
        else
            for i = 1:numClasses
                isMostProbable = (predLabels==classLabels{i});
                if ~all(isMostProbable==isMostProbable(1));
                    contour(x1Grid,x2Grid,reshape(isMostProbable,size(x1Grid)),...
                            [0.5 0.5],'-','LineWidth',2,'color',groupColors{i});
                end
            end
        end
    end
end

%-------------------------------------------------------------------------------
% Set Legend
%-------------------------------------------------------------------------------
if numClasses > 1
    legendText = cell(numClasses,1);
    for i = 1:numClasses
        if ~isempty(classLabels)
            legendText{i} = sprintf('%s (%u)',classLabels{i},sum(groupLabels==classLabels{i}));
        else
            legendText{i} = sprintf('Group %u (%u)',i,sum(groupLabels==classLabels{i}));
        end
    end
    legend([handles{:}],legendText,'interpreter','none','Location','best','AutoUpdate','off');
end

%-------------------------------------------------------------------------------
% Relabel axes with classification rates and set title if classification
% performed successfully:
%-------------------------------------------------------------------------------
if didClassify
    labelAxes(1);
    title(sprintf('Combined classification rate (%s) = %.2f%%',cfnParams.whatClassifier, ...
                    round(classRate(3,1))),'interpreter','none');
end

%-------------------------------------------------------------------------------
%-------------------------------------------------------------------------------
function labelAxes(didClassify)
    % Label Axes
    if didClassify
        labelText = cell(2,1);
        for i = 1:2
            labelText{i} = sprintf('%s (acc = %.2f %%)',featureLabels{i}, ...
                                    round(classRate(i,1)));
        end
    else
        labelText = featureLabels;
    end

    xlabel(labelText{1},'interpreter','none')
    ylabel(labelText{2},'interpreter','none')
end
%-------------------------------------------------------------------------------

end


function [subTSeries, subTrials] = er_dataSubset(mv, runs);
% Grab time series and trials (event-related design) data for the
% selected runs only from the mv struct.
%
% [subTSeries, subTrials] = er_dataSubset(mv, runs);
%
% ras, 06/30/08. Originally written circa 2005, but I found it generally
% useful and broke it off from mv_amps.
allRuns = unique(mv.trials.run);

% Part 1: Need to find rows in tSeries
% corresponding to the selected runs:
%
tr = mv.params.framePeriod;
nConds = sum(mv.trials.condNums>0);
nFrames = size(mv.tSeries,1);

% first construct an index, for each frame of the tSeries,
% of the run that frame came from:
runIndex = zeros(1,nFrames);

firstTrials = find(nVals(mv.trials.run)==1);
firstFrames = mv.trials.onsetFrames(firstTrials);
runIndex(firstFrames) = mv.trials.run(firstTrials);

% fill in each unassigned entry in the index
% with the current run (given in the previous entry):
% NOTE: possible bug below: runIndex(I) = runIndex(I-1);
% this works if all runs have the same # of frames, but if
% using some short runs, may overwrite prev runs. Need to
% subsample I to find runIndex(I)==0. I'm waiting to
% correct this until I get time to thoroughly check it.
I = intersect(firstFrames+1,1:nFrames); % restrict to max # frames
while any(runIndex(I)==0)
    runIndex(I) = runIndex(I-1);
    I = intersect(I+1, 1:nFrames);
end

% now grab the appropriate rows from the tSeries:
ind = find(ismember(runIndex, runs));
subTSeries = mv.tSeries(ind,:);

% part 2: get event onset/condition data
% for selected runs in subTrials struct:
%
subTrials = mv.trials;
if ~isequal(runs,allRuns)
    % subTrials needs to reflect only selected runs,
    % (tricky):

    % First, find the # of frames in each run:
    lastTrials = [firstTrials(2:end)-1 length(mv.trials.onsetFrames)];
    lastTrialFrames = mv.trials.onsetFrames(lastTrials);
    framesPerRun = [lastTrialFrames(1) diff(lastTrialFrames)];

    % Next, select event data only for selected subsets:
    ok = find(ismember(subTrials.run,runs));
    subTrials.onsetSecs = subTrials.onsetSecs(ok);
    subTrials.onsetFrames = subTrials.onsetFrames(ok);
    subTrials.cond = subTrials.cond(ok);
    subTrials.label = subTrials.label(ok);
    subTrials.run = subTrials.run(ok);
    subTrials.parfiles = subTrials.parfiles(runs);

    % Lastly, correct the onset information: since onsets
    % are counted cumulatively, they will reflect
    % non-selected runs. Correct for this overcounting:
    skippedRuns = setdiff(allRuns,runs);
    for j = skippedRuns
        offset = framesPerRun(j);
        laterRuns = find(subTrials.run>j);
        subTrials.onsetFrames(laterRuns) = ...
            subTrials.onsetFrames(laterRuns) - offset;
        subTrials.onsetSecs(laterRuns) = ...
            subTrials.onsetSecs(laterRuns) - offset*tr;
    end
end

return


function [prot,rstraj] = read_twix_hdr(fid)

% function to read raw data header information from siemens MRI scanners
% (currently VB and VD software versions are supported and tested).
%
% Author: Philipp Ehses (philipp.ehses@tuebingen.mpg.de), Mar/11/2014

    nbuffers = fread(fid, 1,'uint32');

    prot = [];
    for b=1:nbuffers
        % Read string up to nul termination
        % ARC 20201020: previous version failed to find nul termination on buffer names > 10 characters
        bl=64;
        bufname = fread(fid, bl, 'uint8=>char').';

        nul_ix=min(find(bufname==0)); % ARC: index of nul termination

        if isempty(nul_ix)
          % This is not a simple buffer name as would be expected.
          % Since we don't have a good strategy for dealing with this, just
          % carry on reading until a nul temrinator is found, then discard this
          % buffer.

          warning('read_twix_hdr: skipping extraordinarily long buffer.');

          % keep reading until we find a nul
          while isempty(nul_ix)
            junk = fread(fid, bl, 'uint8=>char').';
            if numel(junk)==0
              error('read_twix_hdr: reached EOF before nul. This is perplexing.');
            end
            nul_ix=min(find(junk==0));
          end

          fseek(fid, nul_ix-bl, 'cof');

          continue; % skip to the next buffer
        end

        bufname=bufname(1:nul_ix-1);
        fseek(fid, numel(bufname)-(bl-1), 'cof');

        buflen         = fread(fid, 1,'uint32');
        buffer         = fread(fid, buflen, 'uint8=>char').';
        buffer         = regexprep(buffer,'\n\s*\n',''); % delete empty lines
        prot.(bufname) = parse_buffer(buffer);
    end

    if nargout>1
        rstraj = [];
        if isfield(prot,'Meas') && isfield(prot.Meas,'alRegridMode') && prot.Meas.alRegridMode(1)>1
            ncol           = prot.Meas.alRegridDestSamples(1);
            dwelltime      = prot.Meas.aflRegridADCDuration(1)/ncol;
            gr_adc         = zeros(1,ncol,'single');
            start          = prot.Meas.alRegridRampupTime(1) - (prot.Meas.aflRegridADCDuration(1)-prot.Meas.alRegridFlattopTime(1))/2;
            time_adc       = start + dwelltime * (0.5:ncol);
            ixUp           = time_adc <= prot.Meas.alRegridRampupTime(1);
            ixFlat         = (time_adc <= prot.Meas.alRegridRampupTime(1)+prot.Meas.alRegridFlattopTime(1)) & ~ixUp;
            ixDn           = ~ixUp & ~ixFlat;
            gr_adc(ixFlat) = 1;
            if prot.Meas.alRegridMode(1) == 2
                % trapezoidal gradient
                gr_adc(ixUp)   = time_adc(ixUp)/prot.Meas.alRegridRampupTime(1);
                gr_adc(ixDn)   = 1 - (time_adc(ixDn)-prot.Meas.alRegridRampupTime(1)-prot.Meas.alRegridFlattopTime(1))/prot.Meas.alRegridRampdownTime(1);
            elseif prot.Meas.alRegridMode(1) == 4
                % sinusoidal gradient
                gr_adc(ixUp)   = sin(pi/2*time_adc(ixUp)/prot.Meas.alRegridRampupTime(1));
                gr_adc(ixDn)   = sin(pi/2*(1+(time_adc(ixDn)-prot.Meas.alRegridRampupTime(1)-prot.Meas.alRegridFlattopTime(1))/prot.Meas.alRegridRampdownTime(1)));
            else
                warning('regridding mode unknown');
                return;
            end
            % make sure that gr_adc is always positive (rstraj needs to be
            % strictly monotonic):
            gr_adc = max(gr_adc,1e-4);
            rstraj = (cumsum(gr_adc(:)) - ncol/2)/sum(gr_adc(:));
            rstraj = rstraj - rstraj(ncol/2+1);
            % scale rstraj by kmax (only works if all slices have same FoV!!!)
            kmax = prot.MeasYaps.sKSpace.lBaseResolution/...
                prot.MeasYaps.sSliceArray.asSlice{1}.dReadoutFOV;
            rstraj = kmax * rstraj;
        end
    end

end

function prot = parse_buffer(buffer)
    [ascconv, xprot] = regexp(buffer,'### ASCCONV BEGIN[^\n]*\n(.*)\s### ASCCONV END ###','tokens','split');

    if ~isempty(ascconv)
        ascconv = ascconv{:}{:};
        prot = parse_ascconv(ascconv);
    else
        prot = struct();
    end

    if ~isempty(xprot)
        xprot = strcat(xprot{:}); % bug fix by Qiuting Wen (added strcat)
        xprot = parse_xprot(xprot);
        if isstruct(xprot)
            name   = cat(1,fieldnames(prot),fieldnames(xprot));
            val    = cat(1,struct2cell(prot),struct2cell(xprot));
            [~,ix] = unique(name);
            prot   = cell2struct(val(ix),name(ix));
        end
    end
end

function xprot = parse_xprot(buffer)
    xprot = [];
    tokens = regexp(buffer, '<Param(?:Bool|Long|String)\."(\w+)">\s*{([^}]*)','tokens');
    tokens = [tokens, regexp(buffer, '<ParamDouble\."(\w+)">\s*{\s*(<Precision>\s*[0-9]*)?\s*([^}]*)','tokens')];
    for m=1:numel(tokens)
        name         = char(tokens{m}(1));
        % field name has to start with letter
        if (~isletter(name(1)))
            name = strcat('x', name);
        end

        value = char(strtrim(regexprep(tokens{m}(end), '("*)|( *<\w*> *[^\n]*)', '')));
        value = regexprep(value, '\s*', ' ');

        try %#ok<TRYNC>
            value = eval(['[' value ']']);  % inlined str2num()
        end

        xprot.(name) = value;
    end
end

function mrprot = parse_ascconv(buffer)
    mrprot = [];
    % [mv] was: vararray = regexp(buffer,'(?<name>\S*)\s*=\s(?<value>\S*)','names');
    vararray = regexp(buffer,'(?<name>\S*)\s*=\s*(?<value>\S*)','names');

    isOctave = exist('OCTAVE_VERSION', 'builtin') ~= 0;

    for var=vararray

        try
            value = eval(['[' var.value ']']);  % inlined str2num()
        catch
            value = var.value;
        end

        % now split array name and index (if present)
        v = regexp(var.name,'(?<name>\w*)\[(?<ix>[0-9]*)\]|(?<name>\w*)','names');

        cnt = 0;
        tmp = cell(2, numel(v));

        breaked = false;
        for k=1:numel(v)
            if isOctave
                vk = v{k};
                if iscell(vk.name)
                    % lazy fix that throws some info away
                    vk.name = vk.name{1};
                    vk.ix   = vk.ix{1};
                end
            else
                vk = v(k);
            end
            if ~isletter(vk.name(1))
                breaked = true;
                break;
            end
            cnt = cnt+1;
            tmp{1,cnt} = '.';
            tmp{2,cnt} = vk.name;

            if ~isempty(vk.ix)
                cnt = cnt+1;
                tmp{1,cnt} = '{}';
                tmp{2,cnt}{1} = 1 + str2double(vk.ix);
            end
        end
        if ~breaked && ~isempty(tmp)
            S = substruct(tmp{:});
            mrprot = subsasgn(mrprot,S,value);
        end
    end
end


function numOut = analyzeWriteHeader(fname, hdr, endianType)
%
% numOut = analyzeWriteHeader(fname, hdr, [endianType])
%
% writes Analyze-format header
%
% fname   - filename 	     (e.g 'spm' or 'spm.img')
%
% numOut       - number of elements successfully written (should be 348)
%
% see also dbh.h (ANALYZE) spm_hread.m and spm_type.m
%

% Default to little-endian
if ~exist('endianType','var') | isempty(endianType)
    endianType = 'ieee-le';
end

% ensure correct suffix {.hdr} and open header file
%
[p,f,e] = fileparts(fname);
fname = fullfile(p, [f '.hdr']);

fid = fopen(fname,'w',endianType);

if (fid == -1),
	error(['Error opening ' fname '. Check that you have write permission.']);
end;

%---------------------------------------------------------------------------
% if hdr.datatype == 1;   hdr.bitpix = 1;  hdr.glmax = 1;        hdr.glmin = 0;	end
% if hdr.datatype == 2;   hdr.bitpix = 8;  hdr.glmax = 255;      hdr.glmin = 0;	end
% if hdr.datatype == 4;   hdr.bitpix = 16; hdr.glmax = 32767;    hdr.glmin = 0;  	end
% if hdr.datatype == 8;   hdr.bitpix = 32; hdr.glmax = (2^31-1); hdr.glmin = 0;	end
% if hdr.datatype == 16;  hdr.bitpix = 32; hdr.glmax = 1;        hdr.glmin = 0;	end
% if hdr.datatype == 64;  hdr.bitpix = 64; hdr.glmax = 1;        hdr.glmin = 0;	end

%---------------------------------------------------------------------------

fseek(fid,0,'bof');

% write (struct) header_key
%
fwrite(fid, 348, 'int32');
fwrite(fid, fixLen(hdr.data_type, 10), 'char' );
fwrite(fid, fixLen(hdr.db_name,18), 'char' );
fwrite(fid, fixLen(hdr.extents,1), 'int32');
fwrite(fid, fixLen(hdr.session_error,1), 'int16');
fwrite(fid, fixLen(hdr.regular,1), 'char' );
fwrite(fid, fixLen(hdr.hkey_un0,1), 'char' );

% write (struct) image_dimension
%---------------------------------------------------------------------------
fseek(fid,40,'bof');

fwrite(fid, fixLen(hdr.dim,8), 'int16');
fwrite(fid, fixLen(hdr.vox_units,4), 'char' );
fwrite(fid, fixLen(hdr.cal_units,8), 'char' );
fwrite(fid, fixLen(hdr.unused1,1), 'int16' );
fwrite(fid, fixLen(hdr.datatype,1), 'int16');
fwrite(fid, fixLen(hdr.bitpix,1), 'int16');
fwrite(fid, fixLen(hdr.dim_un0,1), 'int16');
fwrite(fid, fixLen(hdr.pixdim,8), 'float');
fwrite(fid, fixLen(hdr.vox_offset,1), 'float');
fwrite(fid, fixLen(hdr.funused1,1),	'float');
fwrite(fid, fixLen(hdr.funused2,1),	'float');
fwrite(fid, fixLen(hdr.funused3,1), 'float');
fwrite(fid, fixLen(hdr.cal_max,1), 'float');
fwrite(fid, fixLen(hdr.cal_min,1), 'float');
fwrite(fid, fixLen(hdr.compressed,1), 'int32');
fwrite(fid, fixLen(hdr.verified,1), 'int32');
fwrite(fid, fixLen(hdr.glmax,1), 'int32');
fwrite(fid, fixLen(hdr.glmin,1), 'int32');

% write (struct) data_history
%---------------------------------------------------------------------------
fwrite(fid, fixLen(hdr.descrip,80),	'char');
fwrite(fid, fixLen(hdr.aux_file,24), 'char');
fwrite(fid, fixLen(hdr.orient,1), 'char');
fwrite(fid, fixLen(hdr.origin,5), 'int16');
fwrite(fid, fixLen(hdr.generated,10), 'uchar');
fwrite(fid, fixLen(hdr.scannum,10), 'uchar');
fwrite(fid, fixLen(hdr.patient_id,10), 'uchar');
fwrite(fid, fixLen(hdr.exp_date,10), 'uchar');
fwrite(fid, fixLen(hdr.exp_time,10), 'uchar');
fwrite(fid, fixLen(hdr.hist_un0,3), 'uchar');
fwrite(fid, fixLen(hdr.views,1), 'int32');
fwrite(fid, fixLen(hdr.vols_added,1), 'int32');
fwrite(fid, fixLen(hdr.start_field,1), 'int32');
fwrite(fid, fixLen(hdr.field_skip,1), 'int32');
fwrite(fid, fixLen(hdr.omax,1), 'int32');
fwrite(fid, fixLen(hdr.omin,1), 'int32');
fwrite(fid, fixLen(hdr.smax,1), 'int32');
fwrite(fid, fixLen(hdr.smin,1), 'int32');

numOut = ftell(fid);
fclose(fid);
return;

function m = fixLen(m, len)
m = m(:)';
if(length(m) > len)
    m = m(1:len);
elseif(length(m) < len)
    if(ischar(m))
        m = [m char(zeros(1,len-length(m)))];
    else
        m = [m zeros(1,len-length(m))];
    end
end
return


function Vo = spm_file_split(V, odir)
% Convert a 4D volume file into a series of 3D volume files
% FORMAT Vo = spm_file_split(V, odir)
% V         - filename or spm_vol struct
% odir      - output directory [default: same as input]
%
% Vo        - spm_vol struct array of output files
%__________________________________________________________________________
% Copyright (C) 2009-2018 Wellcome Trust Centre for Neuroimaging

% Guillaume Flandin
% $Id: spm_file_split.m 7391 2018-08-13 09:55:17Z guillaume $

if ~nargin
    [V, sts] = spm_select(1,'nifti','Select a 4D volume file to split');
    if ~sts, return; end
end
if ischar(V)
    V = spm_vol(spm_file(V,'number',''));
end

[p,n,e] = spm_fileparts(V(1).fname);
if nargin < 2
    if isempty(p), p = pwd; end
    odir = p;
end

Voo = cell(numel(V),1);
spm_progress_bar('Init',numel(V),'Splitting 4D Volume','Volumes Complete');
for i=1:numel(V)
    Voo{i}         = fullfile(odir,sprintf('%s_%05d%s',n,i,e));
    ni             = nifti;
    ni.dat         = file_array(Voo{i},V(i).dim(1:3),V(i).dt,0,V(i).pinfo(1),V(i).pinfo(2));
    ni.mat         = V(i).mat;
    ni.mat0        = V(i).mat;
    ni.mat_intent  = V(i).private.mat_intent;
    ni.mat0_intent = V(i).private.mat0_intent;
    ni.descrip     = [V(i).descrip sprintf(' - %d',i)];
    create(ni);
    ni.dat(:,:,:)  = V(i).private.dat(:,:,:,i);
    spm_progress_bar('Set',i);
end
spm_progress_bar('Clear');

if nargout
    Vo = spm_vol(char(Voo));
end


function tests = tapas_physio_findpeaks_test()
% Tests whether current findpeaks function of Matlab's signal processing
% toolbox delivers same results as previous version used in reference data
%
%    tests = tapas_physio_findpeaks_test()
%
% This relies on the GE/PPU3T dataset which is particularly challenging in
% terms of peak detection because of low SNR and drift of the PPU signal
%
% IN
%
% OUT
%
% EXAMPLE
%   tapas_physio_findpeaks_test
%
%   See also

% Author:   Lars Kasper
% Created:  2019-03-14
% Copyright (C) 2019 TNU, Institute for Biomedical Engineering,
%                    University of Zurich and ETH Zurich.
%
% This file is part of the TAPAS PhysIO Toolbox, which is released under
% the terms of the GNU General Public License (GPL), version 3. You can
% redistribute it and/or modify it under the terms of the GPL (either
% version 3 or, at your option, any later version). For further details,
% see the file COPYING or <http://www.gnu.org/licenses/>.

tests = functiontests(localfunctions);
end

function test_ge_ppu3t_peaks(testCase)
%% Compares previously saved cpulse (detected cardiac pulses) from
% physio-structure to same output when re-running current version of
% GE PPU3T example
% both SPM or matlab-script based execution is possible (check parameter
% doUseSpm below!)

doUseSpm = true;

% run GE example and extract physio
pathPhysioPublic = fullfile(fileparts(mfilename('fullpath')), '..', '..', '..');
% TODO: Make generic!
pathExamples =  fullfile(pathPhysioPublic, '..', 'examples');

if doUseSpm
    pathCurrentExample = fullfile(pathExamples, 'GE/PPU3T');
    pathNow = pwd;
    cd(pathCurrentExample); % for prepending absolute paths correctly
    fileExample = fullfile(pathCurrentExample, 'ge_ppu3t_spm_job.mat');
    load(fileExample, 'matlabbatch');

    % remove unnecessary verbosity and processing of resp data
    matlabbatch{1}.spm.tools.physio.verbose.level = 0;
    matlabbatch{1}.spm.tools.physio.log_files.respiration = {''};

    spm_jobman('run', matlabbatch);
    cd(pathNow);

    % retrieve physio struct from saved file
    matlabbatch{1}.spm.tools.physio.model.output_physio = fullfile(pathCurrentExample, ...
        matlabbatch{1}.spm.tools.physio.save_dir{1}, ...
        matlabbatch{1}.spm.tools.physio.model.output_physio);
    load(matlabbatch{1}.spm.tools.physio.model.output_physio, 'physio');
    actPhysio = physio;
else % has verbosity...cannot switch it off
    fileExample = fullfile(pathExamples, 'GE/PPU3T/ge_ppu3t_matlab_script.m');
    run(fileExample); % will output a PhysIO=struct
    actPhysio = physio;
end

% load physio from reference data
fileReferenceData = fullfile(pathExamples, 'TestReferenceResults', 'utils', ...
    'physio_findpeaks_ge_ppu3t.mat');
load(fileReferenceData, 'physio');
expPhysio = physio;

% extract cpulse from actual and expected solution and compare
actSolution = actPhysio.ons_secs.cpulse;
expSolution = expPhysio.ons_secs.cpulse;

verifyEqual(testCase, actSolution, expSolution);

end

function test_ge_ppu3t_findpeaks_compatible(testCase)
%% Removes signal processing findpeaks from path and checks whether
% deprecated modified version from older Matlab release delivers same
% results
% Is also true, when no tapas_physio_findpeaks_compatible function exists

pathSignalToolbox = fileparts(which('findpeaks'));

hasCompatibleFindPeaks = ~isempty(which('tapas_physio_findpeaks_compatible'));

if ~isempty(pathSignalToolbox) && hasCompatibleFindPeaks
    rmpath(pathSignalToolbox);
    fprintf('\n\t!!! Temporarily removed %s from path to check performance vs legacy findpeaks\n', ...
        pathSignalToolbox);
    test_ge_ppu3t_peaks(testCase);
    addpath(pathSignalToolbox);
else
    assumeEqual(testCase, 1, 1);
end
end


function mpc = toggle_dcline(mpc, on_off)
%TOGGLE_DCLINE Enable, disable or check status of DC line modeling.
%   MPC = TOGGLE_DCLINE(MPC, 'on')
%   MPC = TOGGLE_DCLINE(MPC, 'off')
%   T_F = TOGGLE_DCLINE(MPC, 'status')
%
%   Enables, disables or checks the status of a set of OPF userfcn
%   callbacks to implement DC lines as a pair of linked generators.
%   While it uses the OPF extension mechanism, this implementation
%   works for simple power flow as well as OPF problems.
%
%   These callbacks expect to find a 'dcline' field in the input MPC,
%   where MPC.dcline is an ndc x 17 matrix with columns as defined
%   in IDX_DCLINE, where ndc is the number of DC lines.
%
%   The 'int2ext' callback also packages up flow results and stores them
%   in appropriate columns of MPC.dcline.
%
%   NOTE: Because of the way this extension modifies the number of
%   rows in the gen and gencost matrices, caution must be taken
%   when using it with other extensions that deal with generators.
%
%   Examples:
%       mpc = loadcase('t_case9_dcline');
%       mpc = toggle_dcline(mpc, 'on');
%       results1 = runpf(mpc);
%       results2 = runopf(mpc);
%
%   See also IDX_DCLINE, ADD_USERFCN, REMOVE_USERFCN, RUN_USERFCN.

%   MATPOWER
%   Copyright (c) 2011-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.

if strcmp(upper(on_off), 'ON')
    %% define named indices into data matrices
    c = idx_dcline;

    %% check for proper input data
    if ~isfield(mpc, 'dcline') || size(mpc.dcline, 2) < c.LOSS1
        error('toggle_dcline: case must contain a ''dcline'' field, an ndc x %d matrix.', c.LOSS1);
    end
    if isfield(mpc, 'dclinecost') && size(mpc.dcline, 1) ~= size(mpc.dclinecost, 1)
        error('toggle_dcline: number of rows in ''dcline'' field (%d) and ''dclinecost'' field (%d) do not match.', ...
            size(mpc.dcline, 1), size(mpc.dclinecost, 1));
    end
    l0 = mpc.dcline(:, c.LOSS0);
    l1 = mpc.dcline(:, c.LOSS1);
    k = find( (l0 + l1 .* mpc.dcline(:, c.PMIN) < 0) | ...
              (l0 + l1 .* mpc.dcline(:, c.PMAX) < 0) );
    if ~isempty(k)
        warning('toggle_dcline: loss can be negative for DC line from bus %d to %d\n', ...
            [mpc.dcline(k, c.F_BUS:c.T_BUS)]');
    end

    %% add callback functions
    %% note: assumes all necessary data included in 1st arg (mpc, om, results)
    %%       so, no additional explicit args are needed
    mpc = add_userfcn(mpc, 'ext2int', @userfcn_dcline_ext2int);
    mpc = add_userfcn(mpc, 'formulation', @userfcn_dcline_formulation);
    mpc = add_userfcn(mpc, 'int2ext', @userfcn_dcline_int2ext);
    mpc = add_userfcn(mpc, 'printpf', @userfcn_dcline_printpf);
    mpc = add_userfcn(mpc, 'savecase', @userfcn_dcline_savecase);
    mpc.userfcn.status.dcline = 1;
elseif strcmp(upper(on_off), 'OFF')
    mpc = remove_userfcn(mpc, 'savecase', @userfcn_dcline_savecase);
    mpc = remove_userfcn(mpc, 'printpf', @userfcn_dcline_printpf);
    mpc = remove_userfcn(mpc, 'int2ext', @userfcn_dcline_int2ext);
    mpc = remove_userfcn(mpc, 'formulation', @userfcn_dcline_formulation);
    mpc = remove_userfcn(mpc, 'ext2int', @userfcn_dcline_ext2int);
    mpc.userfcn.status.dcline = 0;
elseif strcmp(upper(on_off), 'STATUS')
    if isfield(mpc, 'userfcn') && isfield(mpc.userfcn, 'status') && ...
            isfield(mpc.userfcn.status, 'dcline')
        mpc = mpc.userfcn.status.dcline;
    else
        mpc = 0;
    end
else
    error('toggle_dcline: 2nd argument must be ''on'', ''off'' or ''status''');
end

%%-----  ext2int  ------------------------------------------------------
function mpc = userfcn_dcline_ext2int(mpc, mpopt, args)
%
%   mpc = userfcn_dcline_ext2int(mpc, mpopt, args)
%
%   This is the 'ext2int' stage userfcn callback that prepares the input
%   data for the formulation stage. It expects to find a 'dcline' field
%   in mpc as described above. The optional args are not currently used.
%   It adds two dummy generators for each in-service DC line, with the
%   appropriate upper and lower generation bounds and corresponding
%   entries in gencost. It also expands columns of any A and N matrices
%   accordingly, if present.

%% define named indices into data matrices
[PQ, PV, REF, NONE, BUS_I, BUS_TYPE, PD, QD, GS, BS, BUS_AREA, VM, ...
    VA, BASE_KV, ZONE, VMAX, VMIN, LAM_P, LAM_Q, MU_VMAX, MU_VMIN] = idx_bus;
[GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS, PMAX, PMIN, ...
    MU_PMAX, MU_PMIN, MU_QMAX, MU_QMIN, PC1, PC2, QC1MIN, QC1MAX, ...
    QC2MIN, QC2MAX, RAMP_AGC, RAMP_10, RAMP_30, RAMP_Q, APF] = idx_gen;
[PW_LINEAR, POLYNOMIAL, MODEL, STARTUP, SHUTDOWN, NCOST, COST] = idx_cost;
c = idx_dcline;

%% initialize some things
if isfield(mpc, 'dclinecost')
    havecost = 1;
else
    havecost = 0;
end

%% save version with external indexing
mpc.order.ext.dcline = mpc.dcline;              %% external indexing
if havecost
    mpc.order.ext.dclinecost = mpc.dclinecost;  %% external indexing
end

%% work with only in-service DC lines
mpc.order.dcline.status.on  = find(mpc.dcline(:, c.BR_STATUS) >  0);
mpc.order.dcline.status.off = find(mpc.dcline(:, c.BR_STATUS) <= 0);

%% remove out-of-service DC lines
dc = mpc.dcline(mpc.order.dcline.status.on, :); %% only in-service DC lines
if havecost
    dcc = mpc.dclinecost(mpc.order.dcline.status.on, :);    %% only in-service DC lines
    mpc.dclinecost = dcc;
end
ndc = size(dc, 1);          %% number of in-service DC lines
o = mpc.order;

%%-----  convert stuff to internal indexing  -----
dc(:, c.F_BUS) = o.bus.e2i(dc(:, c.F_BUS));
dc(:, c.T_BUS) = o.bus.e2i(dc(:, c.T_BUS));
mpc.dcline = dc;

%%-----  create gens to represent DC line terminals  -----
%% ensure consistency of initial values of PF, PT and losses
%% (for simple power flow cases)
dc(:, c.PT) = dc(:, c.PF) - (dc(:, c.LOSS0) + dc(:, c.LOSS1) .* dc(:, c.PF));

%% create gens
fg = zeros(ndc, size(mpc.gen, 2));
fg(:, MBASE)        = 100;
fg(:, GEN_STATUS)   =  dc(:, c.BR_STATUS);  %% status (should be all 1's)
fg(:, PMIN)         = -Inf;
fg(:, PMAX)         =  Inf;
tg = fg;
fg(:, GEN_BUS)      =  dc(:, c.F_BUS);      %% from bus
tg(:, GEN_BUS)      =  dc(:, c.T_BUS);      %% to bus
fg(:, PG)           = -dc(:, c.PF);         %% flow (extracted at "from")
tg(:, PG)           =  dc(:, c.PT);         %% flow (injected at "to")
fg(:, QG)           =  dc(:, c.QF);         %% VAr injection at "from"
tg(:, QG)           =  dc(:, c.QT);         %% VAr injection at "to"
fg(:, VG)           =  dc(:, c.VF);         %% voltage set-point at "from"
tg(:, VG)           =  dc(:, c.VT);         %% voltage set-point at "to"
k = find(dc(:, c.PMIN) >= 0);           %% min positive direction flow
if ~isempty(k)                              %% contrain at "from" end
    fg(k, PMAX)     = -dc(k, c.PMIN);       %% "from" extraction lower lim
end
k = find(dc(:, c.PMAX) >= 0);           %% max positive direction flow
if ~isempty(k)                              %% contrain at "from" end
    fg(k, PMIN)     = -dc(k, c.PMAX);       %% "from" extraction upper lim
end
k = find(dc(:, c.PMIN) < 0);            %% max negative direction flow
if ~isempty(k)                              %% contrain at "to" end
    tg(k, PMIN)     =  dc(k, c.PMIN);       %% "to" injection lower lim
end
k = find(dc(:, c.PMAX) < 0);            %% min negative direction flow
if ~isempty(k)                              %% contrain at "to" end
    tg(k, PMAX)     =  dc(k, c.PMAX);       %% "to" injection upper lim
end
fg(:, QMIN)         =  dc(:, c.QMINF);      %% "from" VAr injection lower lim
fg(:, QMAX)         =  dc(:, c.QMAXF);      %% "from" VAr injection upper lim
tg(:, QMIN)         =  dc(:, c.QMINT);      %%  "to"  VAr injection lower lim
tg(:, QMAX)         =  dc(:, c.QMAXT);      %%  "to"  VAr injection upper lim

%% fudge PMAX a bit if necessary to avoid triggering
%% dispatchable load constant power factor constraints
fg(isload(fg), PMAX) = -1e-6;
tg(isload(tg), PMAX) = -1e-6;

%% set all terminal buses to PV (except ref bus)
refbus = find(mpc.bus(:, BUS_TYPE) == REF);
mpc.bus(dc(:, c.F_BUS), BUS_TYPE) = PV;
mpc.bus(dc(:, c.T_BUS), BUS_TYPE) = PV;
mpc.bus(refbus, BUS_TYPE) = REF;

%% expand A and N, if present
nb = size(mpc.bus, 1);
ng = size(mpc.gen, 1);
if isfield(mpc, 'A') && ~isempty(mpc.A)
    [mA, nA] = size(mpc.A);
    if nA >= 2*nb + 2*ng    %% assume AC dimensions
        mpc.A = [   mpc.A(:, 1:2*nb+ng)      sparse(mA, 2*ndc) ...
                    mpc.A(:, 2*nb+ng+(1:ng)) sparse(mA, 2*ndc) ...
                    mpc.A(:, (2*nb+2*ng+1:nA)) ];
    else                    %% assume DC dimensions
        mpc.A = [   mpc.A(:, 1:nb+ng)   sparse(mA, 2*ndc) ...
                    mpc.A(:, (nb+ng+1:nA)) ];
    end
end
if isfield(mpc, 'N') && ~isempty(mpc.N)
    [mN, nN] = size(mpc.N);
    if nN >= 2*nb + 2*ng    %% assume AC dimensions
        mpc.N = [   mpc.N(:, 1:2*nb+ng)      sparse(mN, 2*ndc) ...
                    mpc.N(:, 2*nb+ng+(1:ng)) sparse(mN, 2*ndc) ...
                    mpc.N(:, (2*nb+2*ng+1:nN)) ];
    else                    %% assume DC dimensions
        mpc.N = [   mpc.N(:, 1:nb+ng)   sparse(mN, 2*ndc) ...
                    mpc.N(:, (nb+ng+1:nN)) ];
    end
end

%% append dummy gens
mpc.gen = [mpc.gen; fg; tg];

%% gencost
if isfield(mpc, 'gencost') && ~isempty(mpc.gencost)
    ngcc = size(mpc.gencost, 2);    %% dimensions of gencost
    [pc, qc] = pqcost(mpc.gencost, ng); %% split out re/active costs
    if havecost                 %% user has provided costs
        ndccc = size(dcc, 2);       %% number of dclinecost columns
        ccc = max([ngcc; ndccc]);   %% number of columns in new gencost
        if ccc > ngcc               %% right zero-pad gencost
            pc = [pc zeros(ng, ccc-ngcc)];
            if ~isempty(qc)         %% pad Qg costs, too
                qc = [qc zeros(ng, ccc-ngcc)];
            end
            ngcc = ccc;
        end

        %% flip function across vertical axis and append to gencost
        %% (PF for DC line = -PG for dummy gen at "from" bus)
        for k = 1:ndc
            if dcc(k, MODEL) == POLYNOMIAL
                nc = dcc(k, NCOST);
                temp = dcc(k, NCOST+(1:nc));
                %% flip sign on coefficients of odd terms
                %% (every other starting with linear term,
                %%  that is, the next to last one)
                temp((nc-1):-2:1) = -temp((nc-1):-2:1);
            else  %% dcc(k, MODEL) == PW_LINEAR
                nc = dcc(k, NCOST);
                temp = dcc(k, NCOST+(1:2*nc));
                %% switch sign on horizontal coordinate
                xx = -temp(1:2:2*nc);
                yy =  temp(2:2:2*nc);
                temp(1:2:2*nc) = xx(end:-1:1);
                temp(2:2:2*nc) = yy(end:-1:1);
            end
            padding = zeros(1, ngcc-NCOST-length(temp));
            gck = [dcc(k, 1:NCOST) temp padding];

            %% append to gencost
            pc = [pc; gck];
        end
        %% define zero cost for "to" end gen
        dcgc = ones(ndc, 1) * [2 0 0 2 zeros(1, ngcc-4)];
    else
        %% use zero cost as default on "from" end gen
        dcgc = ones(ndc, 1) * [2 0 0 2 zeros(1, ngcc-4)];
        pc = [pc; dcgc];
    end
    %% always use zero cost on "to" end gen
    pc = [pc; dcgc];

    %% reassemble gencost
    if ~isempty(qc)
        qc = [qc; dcgc; dcgc];  %% always use zero cost Qg, both ends
        mpc.gencost = [pc; qc];
    else
        mpc.gencost = pc;
    end
end

%%-----  formulation  --------------------------------------------------
function om = userfcn_dcline_formulation(om, mpopt, args)
%
%   om = userfcn_dcline_formulation(om, mpopt, args)
%
%   This is the 'formulation' stage userfcn callback that defines the
%   user constraints for the dummy generators representing DC lines.
%   It expects to find a 'dcline' field in the mpc stored in om, as
%   described above. By the time it is passed to this callback,
%   MPC.dcline should contain only in-service lines and the from and
%   two bus columns should be converted to internal indexing. The
%   optional args are not currently used.
%
%   If Pf, Pt and Ploss are the flow at the "from" end, flow at the
%   "to" end and loss respectively, and L0 and L1 are the linear loss
%   coefficients, the the relationships between them is given by:
%       Pf - Ploss = Pt
%       Ploss = L0 + L1 * Pf
%   If Pgf and Pgt represent the injections of the dummy generators
%   representing the DC line injections into the network, then
%   Pgf = -Pf and Pgt = Pt, and we can combine all of the above to
%   get the following constraint on Pgf ang Pgt:
%       -Pgf - (L0 - L1 * Pgf) = Pgt
%   which can be written:
%       -L0 <= (1 - L1) * Pgf + Pgt <= -L0

%% define named indices into data matrices
c = idx_dcline;

%% initialize some things
mpc = om.get_mpc();
dc = mpc.dcline;
ndc = size(dc, 1);              %% number of in-service DC lines
ng  = size(mpc.gen, 1) - 2*ndc; %% number of original gens/disp loads

%% constraints
nL0 = -dc(:, c.LOSS0) / mpc.baseMVA;
L1  =  dc(:, c.LOSS1);
Adc = [sparse(ndc, ng) spdiags(1-L1, 0, ndc, ndc) speye(ndc, ndc)];

%% add them to the model
om.add_lin_constraint('dcline', Adc, nL0, nL0, {'Pg'});

%%-----  int2ext  ------------------------------------------------------
function results = userfcn_dcline_int2ext(results, mpopt, args)
%
%   results = userfcn_dcline_int2ext(results, mpopt, args)
%
%   This is the 'int2ext' stage userfcn callback that converts everything
%   back to external indexing and packages up the results. It expects to
%   find a 'dcline' field in the results struct as described for mpc
%   above. It also expects that the last 2*ndc entries in the gen and
%   gencost matrices correspond to the in-service DC lines (where ndc is
%   the number of rows in MPC.dcline. These extra rows are removed from
%   gen and gencost and the flow is taken from the PG of these gens and
%   placed in the flow column of the appropiate dcline row. Corresponding
%   columns are also removed from any A and N matrices, if present. The
%   optional args are not currently used.

%% define named indices into data matrices
[GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS, PMAX, PMIN, ...
    MU_PMAX, MU_PMIN, MU_QMAX, MU_QMIN, PC1, PC2, QC1MIN, QC1MAX, ...
    QC2MIN, QC2MAX, RAMP_AGC, RAMP_10, RAMP_30, RAMP_Q, APF] = idx_gen;
c = idx_dcline;

%% initialize some things
o = results.order;
k = find(o.ext.dcline(:, c.BR_STATUS));
ndc = length(k);                    %% number of in-service DC lines
ng  = size(results.gen, 1) - 2*ndc; %% number of original gens/disp loads
nb  = size(results.bus, 1);

%% extract dummy gens
fg = results.gen(ng    +(1:ndc), :);
tg = results.gen(ng+ndc+(1:ndc), :);

%% remove dummy gens
results.gen     = results.gen(1:ng, :);
if isfield(results, 'gencost') && ~isempty(results.gencost)
    results.gencost = results.gencost(1:ng, :);
end

%% delete corresponding columns from A and N, if present
if isfield(results, 'A') && ~isempty(results.A)
    [mA, nA] = size(results.A);
    if nA >= 2*nb + 2*ng + 4*ndc    %% assume AC dimensions
        results.A = results.A(:, [1:2*nb+ng 2*nb+ng+2*ndc+(1:ng) 2*nb+2*ng+4*ndc+1:nA]);
    else                            %% assume DC dimensions
        results.A = results.A(:, [1:nb+ng nb+ng+2*ndc+1:nA]);
    end
end
if isfield(results, 'N') && ~isempty(results.N)
    [mN, nN] = size(results.N);
    if nN >= 2*nb + 2*ng + 4*ndc    %% assume AC dimensions
        results.N = results.N(:, [1:2*nb+ng 2*nb+ng+2*ndc+(1:ng) 2*nb+2*ng+4*ndc+1:nN]);
    else                            %% assume DC dimensions
        results.N = results.N(:, [1:nb+ng nb+ng+2*ndc+1:nN]);
    end
end

%% get the solved flows
results.dcline(:, c.PF) = -fg(:, PG);
results.dcline(:, c.PT) =  tg(:, PG);
results.dcline(:, c.QF) =  fg(:, QG);
results.dcline(:, c.QT) =  tg(:, QG);
results.dcline(:, c.VF) =  fg(:, VG);
results.dcline(:, c.VT) =  tg(:, VG);
if size(fg, 2) >= MU_QMIN
    results.dcline(:, c.MU_PMIN ) = fg(:, MU_PMAX) + tg(:, MU_PMIN);
    results.dcline(:, c.MU_PMAX ) = fg(:, MU_PMIN) + tg(:, MU_PMAX);
    results.dcline(:, c.MU_QMINF) = fg(:, MU_QMIN);
    results.dcline(:, c.MU_QMAXF) = fg(:, MU_QMAX);
    results.dcline(:, c.MU_QMINT) = tg(:, MU_QMIN);
    results.dcline(:, c.MU_QMAXT) = tg(:, MU_QMAX);
end

%%-----  convert stuff back to external indexing  -----
results.order.int.dcline = results.dcline;  %% save internal version
%% copy results to external version
o.ext.dcline(k, c.PF:c.VT) = results.dcline(:, c.PF:c.VT);
if size(results.dcline, 2) == c.MU_QMAXT
    o.ext.dcline(k, c.MU_PMIN:c.MU_QMAXT) = results.dcline(:, c.MU_PMIN:c.MU_QMAXT);
end
results.dcline = o.ext.dcline;              %% use external version

%%-----  printpf  ------------------------------------------------------
function results = userfcn_dcline_printpf(results, fd, mpopt, args)
%
%   results = userfcn_dcline_printpf(results, fd, mpopt, args)
%
%   This is the 'printpf' stage userfcn callback that pretty-prints the
%   results. It expects a results struct, a file descriptor and a MATPOWER
%   options struct. The optional args are not currently used.

%% define named indices into data matrices
c = idx_dcline;

%% options
SUPPRESS        = mpopt.out.suppress_detail;
if SUPPRESS == -1
    if size(results.bus, 1) > 500
        SUPPRESS = 1;
    else
        SUPPRESS = 0;
    end
end
OUT_ALL = mpopt.out.all;
OUT_BRANCH      = OUT_ALL == 1 || (OUT_ALL == -1 && ~SUPPRESS && mpopt.out.branch);
if OUT_ALL == -1
    OUT_ALL_LIM = ~SUPPRESS * mpopt.out.lim.all;
elseif OUT_ALL == 1
    OUT_ALL_LIM = 2;
else
    OUT_ALL_LIM = 0;
end
if OUT_ALL_LIM == -1
    OUT_LINE_LIM    = ~SUPPRESS * mpopt.out.lim.line;
else
    OUT_LINE_LIM    = OUT_ALL_LIM;
end
ctol = mpopt.opf.violation; %% constraint violation tolerance
ptol = 1e-4;                %% tolerance for displaying shadow prices

%%-----  print results  -----
dc = results.dcline;
ndc = size(dc, 1);
kk = find(dc(:, c.BR_STATUS) ~= 0);
if OUT_BRANCH
    fprintf(fd, '\n================================================================================');
    fprintf(fd, '\n|     DC Line Data                                                             |');
    fprintf(fd, '\n================================================================================');
    fprintf(fd, '\n Line    From     To        Power Flow           Loss     Reactive Inj (MVAr)');
    fprintf(fd, '\n   #      Bus     Bus   From (MW)   To (MW)      (MW)       From        To   ');
    fprintf(fd, '\n------  ------  ------  ---------  ---------  ---------  ---------  ---------');
    loss = 0;
    for k = 1:ndc
        if dc(k, c.BR_STATUS)   %% status on
            fprintf(fd, '\n%5d%8d%8d%11.2f%11.2f%11.2f%11.2f%11.2f', ...
                        k, dc(k, c.F_BUS:c.T_BUS), dc(k, c.PF:c.PT), ...
                        dc(k, c.PF) - dc(k, c.PT), dc(k, c.QF:c.QT) );
            loss = loss + dc(k, c.PF) - dc(k, c.PT);
        else
            fprintf(fd, '\n%5d%8d%8d%11s%11s%11s%11s%11s', ...
                        k, dc(k, c.F_BUS:c.T_BUS), '-  ', '-  ', '-  ', '-  ', '-  ');
        end
    end
    fprintf(fd, '\n                                              ---------');
    fprintf(fd, '\n                                     Total:%11.2f\n', loss);
end

if OUT_LINE_LIM == 2 || (OUT_LINE_LIM == 1 && ...
        (any(dc(kk, c.PF) > dc(kk, c.PMAX) - ctol) || ...
         any(dc(kk, c.MU_PMIN) > ptol) || ...
         any(dc(kk, c.MU_PMAX) > ptol)))
    fprintf(fd, '\n================================================================================');
    fprintf(fd, '\n|     DC Line Constraints                                                      |');
    fprintf(fd, '\n================================================================================');
    fprintf(fd, '\n Line    From     To          Minimum        Actual Flow       Maximum');
    fprintf(fd, '\n   #      Bus     Bus    Pmin mu     Pmin       (MW)       Pmax      Pmax mu ');
    fprintf(fd, '\n------  ------  ------  ---------  ---------  ---------  ---------  ---------');
    for k = 1:ndc
        if OUT_LINE_LIM == 2 || (OUT_LINE_LIM == 1 && ...
                (dc(k, c.PF) > dc(k, c.PMAX) - ctol || ...
                 dc(k, c.MU_PMIN) > ptol || ...
                 dc(k, c.MU_PMAX) > ptol))
            if dc(k, c.BR_STATUS)   %% status on
                fprintf(fd, '\n%5d%8d%8d', k, dc(k, c.F_BUS:c.T_BUS) );
                if dc(k, c.MU_PMIN) > ptol
                    fprintf(fd, '%11.3f', dc(k, c.MU_PMIN) );
                else
                    fprintf(fd, '%11s', '-  ' );
                end
                fprintf(fd, '%11.2f%11.2f%11.2f', ...
                            dc(k, c.PMIN), dc(k, c.PF), dc(k, c.PMAX) );
                if dc(k, c.MU_PMAX) > ptol
                    fprintf(fd, '%11.3f', dc(k, c.MU_PMAX) );
                else
                    fprintf(fd, '%11s', '-  ' );
                end
            else
                fprintf(fd, '\n%5d%8d%8d%11s%11s%11s%11s%11s', ...
                            k, dc(k, c.F_BUS:c.T_BUS), '-  ', '-  ', '-  ', '-  ', '-  ');
            end
        end
    end
    fprintf(fd, '\n');
end

%%-----  savecase  -----------------------------------------------------
function mpc = userfcn_dcline_savecase(mpc, fd, prefix, args)
%
%   mpc = userfcn_dcline_savecase(mpc, fd, prefix, args)
%
%   This is the 'savecase' stage userfcn callback that prints the M-file
%   code to save the 'dcline' field in the case file. It expects a
%   MATPOWER case struct (mpc), a file descriptor and variable prefix
%   (usually 'mpc.'). The optional args are not currently used.

%% define named indices into data matrices
c = idx_dcline;

%% save it
ncols = size(mpc.dcline, 2);
fprintf(fd, '\n%%%%-----  DC Line Data  -----%%%%\n');
if ncols < c.MU_QMAXT
    fprintf(fd, '%%\tfbus\ttbus\tstatus\tPf\tPt\tQf\tQt\tVf\tVt\tPmin\tPmax\tQminF\tQmaxF\tQminT\tQmaxT\tloss0\tloss1\n');
else
    fprintf(fd, '%%\tfbus\ttbus\tstatus\tPf\tPt\tQf\tQt\tVf\tVt\tPmin\tPmax\tQminF\tQmaxF\tQminT\tQmaxT\tloss0\tloss1\tmuPmin\tmuPmax\tmuQminF\tmuQmaxF\tmuQminT\tmuQmaxT\n');
end
template = '\t%d\t%d\t%d\t%.9g\t%.9g\t%.9g\t%.9g\t%.9g\t%.9g\t%.9g\t%.9g\t%.9g\t%.9g\t%.9g\t%.9g\t%.9g\t%.9g';
if ncols == c.MU_QMAXT
    template = [template, '\t%.4f\t%.4f\t%.4f\t%.4f\t%.4f\t%.4f'];
end
template = [template, ';\n'];
fprintf(fd, '%sdcline = [\n', prefix);
fprintf(fd, template, mpc.dcline.');
fprintf(fd, '];\n');

%% to do, make it save mpc.dclinecost too (somehow I forgot this)
%% have a look at the saving of gencost in savecase for template


function varargout = rdivide(varargin)
%./   Pointwise right divide of SPHEREFUN objects.
%   F./G if F is a SPHEREFUN and G is a double this returns (1/G)*F
%
%   F./G if F is a double and G is a v this returns F/G, but this doe
%   not work if G becomes numerically close to zero.
%
%   F./G we do not allow F and G to both be SPHEREFUN object.
%
%   F./G is the same as the command rdivide(F,G)
%
% See also SPHEREFUN/LDIVIDE.

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

[varargout{1:nargout}] = rdivide@separableApprox(varargin{:});

end


function [varargout] = VBA_spm_unvec(vX,varargin)
% Unvectorise a vectorised array - a compiled routine
% FORMAT [varargout] = spm_unvec(vX,varargin)
% varargin  - numeric, cell or structure array
% vX        - spm_vec(X)
%
% i.e. X           = spm_unvec(spm_vec(X),X)
%      [X1,X2,...] = spm_unvec(spm_vec(X1,X2,...),X1,X2,...)
%
% See spm_vec
%__________________________________________________________________________
% Copyright (C) 2005-2014 Wellcome Trust Centre for Neuroimaging

% Karl Friston
% $Id: spm_unvec.m 6151 2014-09-03 18:09:33Z guillaume $

%error('spm_unvec.c not compiled - see Makefile')

% deal to multiple outputs if necessary
%--------------------------------------------------------------------------
if nargout > 1
    varargout = VBA_spm_unvec(vX,varargin);
    return
end
if numel(varargin) == 1
    X  = varargin{1};
else
    X  = varargin;
end

% reshape numerical arrays
%--------------------------------------------------------------------------
if isnumeric(X) || islogical(X)
    if ndims(X) > 2
        X(:)  = full(vX);
    else
        X(:)  = vX;
    end
    varargout = {X};
    return
end

% fill in structure arrays
%--------------------------------------------------------------------------
if isstruct(X)
    f = fieldnames(X);
    for i = 1:numel(f)
        c          = {X.(f{i})};
        if isnumeric(c)
            n      = numel(c);
        else
            n      = VBA_spm_length(c);
        end
        c          = VBA_spm_unvec(vX(1:n),c);
        [X.(f{i})] = deal(c{:});
        vX         = vX(n + 1:end);
    end
    varargout      = {X};
    return
end

% fill in cell arrays
%--------------------------------------------------------------------------
if iscell(X)
    for i = 1:numel(X)
        if isnumeric(X{i})
            n      = numel(X{i});
        else
            n      = VBA_spm_length(X{i});
        end
        X{i}  = VBA_spm_unvec(vX(1:n),X{i});
        vX    = vX(n + 1:end);
    end
    varargout = {X};
    return
end

% else
%--------------------------------------------------------------------------
X         = [];
varargout = {X};


classdef PTKUnclosedLungIncludingTrachea < PTKPlugin
    % PTKUnclosedLungIncludingTrachea. Plugin to segment the lung regions
    %     including the airways.
    %
    %     This is a plugin for the Pulmonary Toolkit. Plugins can be run using
    %     the gui, or through the interfaces provided by the Pulmonary Toolkit.
    %     See PTKPlugin.m for more information on how to run plugins.
    %
    %     Plugins should not be run directly from your code.
    %
    %
    %     Licence
    %     -------
    %     Part of the TD Pulmonary Toolkit. https://github.com/tomdoel/pulmonarytoolkit
    %     Author: Tom Doel, 2012.  www.tomdoel.com
    %     Distributed under the GNU GPL v3 licence. Please see website for details.
    %

    properties
        ButtonText = 'Lungs with trachea';
        ToolTip = 'Find unclosed lung region including the trachea and main bronchi'
        Category = 'Lungs'

        AllowResultsToBeCached = true
        AlwaysRunPlugin = false
        PluginType = 'ReplaceOverlay'
        HidePluginInDisplay = false
        FlattenPreviewImage = false
        PTKVersion = '1'
        ButtonWidth = 6
        ButtonHeight = 2
        GeneratePreview = true
        Visibility = 'Developer'
        Version = 3
    end

    methods (Static)
        function results = RunPlugin(dataset, reporting)
            if dataset.IsGasMRI
                results = dataset.GetResult('PTKSegmentGasMRI');
                results.AddBorder(1);
                results = PTKGetMainRegionExcludingBorder(results, 1000000, reporting);
                results.RemoveBorder(1);
            elseif strcmp(dataset.GetImageInfo.Modality, 'MR')
                lung_threshold = dataset.GetResult('PTKMRILungThreshold');
                results = lung_threshold.LungMask;

            else
                threshold_image = dataset.GetResult('PTKThresholdLungFiltered');
                threshold_image.ChangeRawImage(threshold_image.RawImage > 0);

                reporting.ShowProgress('Searching for largest connected region');

                % Find the main component, excluding any components touching the border
                original_image = dataset.GetResult('PTKLungROI');
                threshold_image = PTKGetMainRegionExcludingPaddingBorder(original_image, threshold_image, 1000000, false, reporting);

                results = threshold_image;
                results.ImageType = PTKImageType.Colormap;
            end
        end
    end
end


function result = outOfChina(lat,long)

    result = [long < 72.004 | long > 137.8347 | lat < 0.8293 | lat > 55.8271];

end