function gamsWrite(prob,filename)
%gamsWrite  Write a GAMS Model from MATLAB data
%
% gamsWrite(prob,filename) writes the OPTI problem to a GAMS model (.gms)
% and optionally a GDX file if SCIP is not used.
%
% You may specify a full path to the file, or if you specify a filename
% only, it will be written to the current directory.

%   Copyright (C) 2013 Jonathan Currie (I2C2)

ext = 'gms';

%Quick Checks
if(~ischar(filename))
    error('Filename must be a char array!');
end
if(~isempty(prob.sdcone))
    error('Semidefinite problems cannot be written by this interface');
end
% Make sure we have file extension for GAMS model
if(isempty(strfind(lower(filename),['.' ext])))
    gdxname = filename;
    filename = [filename '.' ext];
else
    % Remove file extension for GDX file
    ind = strfind(lower(filename),['.' ext]);
    gdxname = filename(1:ind-1);
end

% Check for full filepath
if(isempty(strfind(filename,':')))
    filename = [cd filesep filename];
    gdxname = [cd filesep gdxname];
end

%If we have SCIP installed, use that (much easier, and does nonlinear models)
if(checkSolver('scip',0))
    opts = optiset('solver','scip','warnings','none','display','off','solverOpts',scipset('gamsfile',filename));
    if(isstruct(prob.int) && isfield(prob.int,'str'))
        prob.int = prob.int.str;
    end
    if(isempty(prob.x0) || all(isnan(prob.x0))), prob.x0 = randn(prob.sizes.ndec,1); end
    Opt = opti(prob,opts);
    try
        solve(Opt);
    catch ME
        if(strcmpi(ME.identifier,'OPTI:SCIPVAR'))
            throwAsCaller(MException('OPTI:SCIPVAR','AN ERROR OCCURED WHILE PRE-PROCESSING YOUR MATLAB FUNCTION(S) - PLEASE EXAMINE THE SCIP ERROR BELOW:\n\n%s',ME.message));
        else
            throw(ME);
        end
    end
    return;
end

%Check not nonlinear
if(~isempty(prob.fun) && isa(prob.fun,'function_handle'))
    error('The OPTI GAMS Writer only works for problems up to MIQCQP complexity.\n\nFor general nonlinear problems you must have %s installed to write nonlinear GAMS problems','SCIP');
end

%Check GAMS routines are available
if(~exist(['rgdx.' mexext],'file') || ~exist(['wgdx.' mexext],'file'))
    error('Could not find the required GAMS routines on your PC [wgdx,rgdx]!\n\n%s',...
          'Please download a current GAMS distribution from "http://www.gams.com/download/", then add the GAMS install folder to the MATLAB path.');
end

%Check for SOS constraints (not yet implemented)
if(~isempty(prob.sos))
    optiwarn('OPTI:GamsWriteSOS','SOS constraints are not currently supported by this interface, so they will be skipped!');
end

%Determine number of decision variables
if(isfield(prob,'sizes'))
    ndec = prob.sizes.ndec;
elseif(~isempty(prob.f))
    ndec = numel(prob.f);
elseif(~isempty(prob.x0))
    ndec = numel(prob.x0);
elseif(~isempty(prob.H))
    ndec = size(prob.H,1);
else
    error('Could not determine the number of decision variables!');
end

%Convert linear constraints to row format
if(~isempty(prob.b) || ~isempty(prob.beq))
    [prob.A,prob.rl,prob.ru] = gen2row(prob.A,prob.b,prob.Aeq,prob.beq);
end

%Convert integer constraints to string
if(~isempty(prob.int) && ~isstruct(prob.int))
    if(ischar(prob.int))
        str = prob.int;
        prob.int = struct('str',str);
    elseif(isnumeric(prob.int))
        str = repmat('C',1,ndec);
        str(prob.int) = 'I';
        prob.int = struct('str',str);
    else
        error('Unknown integer format');
    end
elseif(isempty(prob.int))
    prob.int.str = [];
end
prob.int.str = upper(prob.int.str);
nint = sum(prob.int.str == 'I');
nbin = sum(prob.int.str == 'B');

%Starting writing GAMS Model File
fid = fopen(filename,'w');
if(fid < 0), error('Error creating GAMS Model: %s',filename); end

name = regexprep(prob.Name,' ','_');

fprintf(fid,'* OPTI Generated GAMS Model (%s)\n',datestr(now));
fprintf(fid,'*   Model Name             : %s\n',name);
if(isfield(prob,'type') && ~isempty(prob.type))
    fprintf(fid,'*   Model Classification   : %s\n',prob.type);
end
fprintf(fid,'*   Variables              : %d (%d Integer, %d Binary)\n',ndec,nint,nbin);
if(~isempty(prob.rl)),  fprintf(fid,'*   Linear Constraints     : %d\n',numel(prob.rl)); end
if(~isempty(prob.qrl)), fprintf(fid,'*   Quadratic Constraints  : %d\n',numel(prob.qrl)); end
if(~isempty(prob.cl)),  fprintf(fid,'*   Nonlinear Constraints  : %d\n',numel(prob.cl)); end
fprintf(fid,'\n');

try
    model = 'lp';
    %Check for integer and binary constraints
    if(~all(prob.int.str == 'C'))
        int = struct('name','int','type','set','dim',1,'uels',{{find(prob.int.str ~= 'C')}},'form','full','ts','integer and binary indices');
        if(any(prob.int.str == 'B'))
            %Force binary bounds
            ind = (prob.int.str == 'B')';
            if(isempty(prob.lb)), prob.lb = -Inf(ndec,1); end
            if(isempty(prob.ub)), prob.ub = Inf(ndec,1); end
            %Set bin indicies which are infinite or bounds > 1 or < 0 to 1/0
            prob.lb((isinf(prob.lb) | (prob.lb < 0)) & ind) = 0;
            prob.ub((isinf(prob.ub) | (prob.ub > 1)) & ind) = 1;
        end
    else
        int = [];
    end
    %Check for bounds
    if(~isempty(prob.lb))
        lb = struct('name','lb','type','parameter','dim',1,'val',full(prob.lb),'form','full','ts','decision variable lower bound');
    else
        lb = [];
    end
    if(~isempty(prob.ub))
        ub = struct('name','ub','type','parameter','dim',1,'val',full(prob.ub),'form','full','ts','decision variable upper bound');
    else
        ub = [];
    end
    %Check for initial guess
    if(~isempty(prob.x0) && all(~isnan(prob.x0)))
        x0 = struct('name','x0','type','parameter','dim',1,'val',full(prob.x0),'form','full','ts','decision variable initial guess');
    else
        x0 = [];
    end
    %Check for linear objective
    if(~isempty(prob.f) && isnumeric(prob.f))
        f = struct('name','f','type','parameter','dim',1,'val',full(prob.f),'form','full','ts','linear objective vector');
    else
        f = [];
    end
    %Check for quadratic objective
    if(~isempty(prob.H) && isnumeric(prob.H))
        %We require all elements of H, see if we have a saved copy before
        %triu/tril
        if(isfield(prob,'save') && isfield(prob.save,'H') && ~isempty(prob.save.H) && isnumeric(prob.save.H))
            H = prob.save.H;
        else
            H = prob.H;
        end
        [i,j,v] = find(sparse(H));
        H = struct('name','H','type','parameter','dim',2,'val',[i j v],'form','sparse','ts','quadratic objective matrix');
        model = 'qp';
    else
        H = [];
    end
    %Check for linear constraints
    if(~isempty(prob.A))
        [i,j,v] = find(sparse(prob.A));
        A = struct('name','A','type','parameter','dim',2,'val',[i j v],'form','sparse','ts','linear constraint matrix');
        rl = struct('name','rl','type','parameter','dim',1,'val',full(prob.rl),'form','full','ts','linear constraint lower bound');
        ru = struct('name','ru','type','parameter','dim',1,'val',full(prob.ru),'form','full','ts','linear constraint upper bound');
    else
        A = []; rl = []; ru = [];
    end
    %Check for quadratic constraints
    if(~isempty(prob.Q))
        %Concatenate each Q matrix into a 3D array
        if(iscell(prob.Q))
            i = []; j = []; k = []; v = [];
            for n = 1:length(prob.Q) %could preallocate...
                [I,J,V] = find(sparse(prob.Q{n}));
                i = [i;I]; %#ok<AGROW>
                j = [j;J]; %#ok<AGROW>
                k = [k;n*ones(size(I))]; %#ok<AGROW>
                v = [v;V]; %#ok<AGROW>
            end
        else
            [i,j,v] = find(sparse(prob.Q));
            k = ones(size(i));
        end
        Q = struct('name','Q','type','parameter','dim',3,'val',[i j k v],'form','sparse','ts','quadratic constraint matrix');
        [i,j,v] = find(sparse(prob.l));
        l = struct('name','l','type','parameter','dim',2,'val',[i j v],'form','sparse','ts','quadratic constraint linear matrix');
        qrl = struct('name','qrl','type','parameter','dim',1,'val',full(prob.qrl),'form','full','ts','quadratic constraint lower bound');
        qru = struct('name','qru','type','parameter','dim',1,'val',full(prob.qru),'form','full','ts','quadratic constraint upper bound');
        model = 'qcqp';
    else
        Q = []; l = []; qrl = []; qru = [];
    end

    %Write GAMS Data
    writeGDX(gdxname,H,f,lb,ub,x0,int,A,rl,ru,Q,l,qrl,qru);

    %Write set definitions
    fprintf(fid,'Sets\n');
    fprintf(fid,' n /1*%d/\n',ndec);
    if(~isempty(int)), fprintf(fid,' ni(n)\n'); end
    if(~isempty(prob.rl)), fprintf(fid,' mlin /1*%d/\n',numel(prob.rl)); end
    if(~isempty(prob.qrl)), fprintf(fid,' mquad /1*%d/\n',numel(prob.qrl)); end
    if(~isempty(prob.cl)), fprintf(fid,' mnlin /1*%d/\n',numel(prob.cl)); end
    fprintf(fid,';\n');

    %Alias for QP, QC
    if(~isempty(H)), fprintf(fid,'Alias (n,n1);\n\n'); end

    %Write parameter definitions
    fprintf(fid,'Parameters\n');
    if(~isempty(H)), fprintf(fid,' H(n,n1)\n'); end
    if(~isempty(f)), fprintf(fid,' f(n)\n'); end
    if(~isempty(lb) && ~isempty(ub))
        fprintf(fid,' lb(n), ub(n)\n');
    elseif(~isempty(lb))
        fprintf(fid,' lb(n)\n');
    elseif(~isempty(ub))
        fprintf(fid,' ub(n)\n');
    end
    if(~isempty(x0)), fprintf(fid,' x0(n)\n'); end
    if(~isempty(A)), fprintf(fid,' A(mlin,n), rl(mlin), ru(mlin)\n'); end
    if(~isempty(Q)), fprintf(fid,' Q(n,n1,mquad), l(n,mquad), qrl(mquad), qru(mquad)\n'); end
    fprintf(fid,';\n');

    %Enter Variables to Import
    fprintf(fid,'* Load Parameters from GDX\n');
    fprintf(fid,'$gdxin %s.gdx\n',gdxname);
    if(~isempty(H)), fprintf(fid,'$load H\n'); end
    if(~isempty(f)), fprintf(fid,'$load f\n'); end
    if(~isempty(lb) && ~isempty(ub))
        fprintf(fid,'$load lb,ub\n');
    elseif(~isempty(lb))
        fprintf(fid,'$load lb\n');
    elseif(~isempty(ub))
        fprintf(fid,'$load ub\n');
    end
    if(~isempty(int)), fprintf(fid,'$load ni = int\n'); end
    if(~isempty(x0)), fprintf(fid,'$load x0\n'); end
    if(~isempty(A)), fprintf(fid,'$load A,rl,ru\n'); end
    if(~isempty(Q)), fprintf(fid,'$load Q,l,qrl,qru\n'); end
    fprintf(fid,'$gdxin\n\n');

    %Complete GAMS file
    %Variable Definition
    fprintf(fid, 'Variables\n x(n)\n');
    fprintf(fid, ' obj;\n\n');

    %Integer and binary def
    if(~isempty(int))
        fprintf(fid,'Integer Variables\n x;\n');
        fprintf(fid,' x.prior(n) = inf;\n');
        fprintf(fid,' x.prior(ni) = 1;\n\n');
    end

    %Equations
    fprintf(fid, 'Equations\n');
    if(~isempty(A))
        fprintf(fid, ' linconLB(mlin)\n');
        fprintf(fid, ' linconUB(mlin)\n');
    end
    if(~isempty(Q))
        fprintf(fid, ' quadconLB(mquad)\n');
        fprintf(fid, ' quadconUB(mquad)\n');
    end
    fprintf(fid, ' cost;\n\n');

    %Objective
    fprintf(fid, '* Cost Function\n');
    switch(model)
        case 'lp'
            fprintf(fid, ' cost.. obj =e= sum(n, f(n)*x(n));\n\n');
        case {'qp','qcqp'}
            fprintf(fid, ' cost.. obj =e= 0.5*sum(n,x(n)*sum(n1,H(n,n1)*x(n1))) + sum(n,f(n)*x(n));\n\n');
        otherwise
            error('Problem type ''%s'' not yet supported',prob.type);
    end

    %Bounds
    if(~isempty(lb) || ~isempty(ub))
        fprintf(fid, '* Decision Variable Bounds\n');
        if(~isempty(lb)), fprintf(fid, ' x.lo(n) = lb(n);\n'); end
        if(~isempty(ub)), fprintf(fid, ' x.up(n) = ub(n);\n'); end
        fprintf(fid,'\n');
    end

    %Initial Guess
    if(~isempty(x0))
        fprintf(fid, '* Initial Solution Guess\n');
        fprintf(fid, ' x.l(n) = x0(n);\n\n');
    end

    %Linear Constraints
    if(~isempty(A))
        fprintf(fid,'* Linear Constraints\n');
        fprintf(fid,' linconLB(mlin).. rl(mlin) =l= sum(n, A(mlin,n)*x(n));\n');
        fprintf(fid,' linconUB(mlin).. sum(n, A(mlin,n)*x(n)) =l= ru(mlin);\n\n');
    end

    %Quadratic Constraints
    if(~isempty(Q))
        fprintf(fid,'* Quadratic Constraints\n');
        fprintf(fid,' quadconLB(mquad).. qrl(mquad) =l= sum(n,x(n)*sum(n1,Q(n,n1,mquad)*x(n1))) + sum(n,l(n,mquad)*x(n));\n');
        fprintf(fid,' quadconUB(mquad).. sum(n,x(n)*sum(n1,Q(n,n1,mquad)*x(n1))) + sum(n,l(n,mquad)*x(n)) =l= qru(mquad);\n\n');
    end

    %Write model declaration
    fprintf(fid,'\nModel %s / all /;\n\n',name);

    %Determine which GAMS solver type to use
    switch(model)
        case 'lp'
            if(isempty(int))
                fprintf(fid,'Solve %s using %s minimizing obj;\n',name,'LP');
            else
                fprintf(fid,'Solve %s using %s minimizing obj;\n',name,'MIP');
            end
        case {'qp','qcqp'}
            if(isempty(int))
                fprintf(fid,'Solve %s using %s minimizing obj;\n',name,'QCP');
            else
                fprintf(fid,'Solve %s using %s minimizing obj;\n',name,'MIQCP');
            end
    end

catch ME
    fclose(fid);
    rethrow(ME);
end

fclose(fid);

function writeGDX(name,H,f,lb,ub,x0,int,A,rl,ru,Q,l,qrl,qru)

args = {};
if(~isempty(H)),    args = [args H]; end
if(~isempty(f)),    args = [args f]; end
if(~isempty(lb)),   args = [args lb]; end
if(~isempty(ub)),   args = [args ub]; end
if(~isempty(x0)),   args = [args x0]; end
if(~isempty(int)),  args = [args int]; end
if(~isempty(A)),    args = [args A]; end
if(~isempty(rl)),   args = [args rl]; end
if(~isempty(ru)),   args = [args ru]; end
if(~isempty(Q)),    args = [args Q]; end
if(~isempty(l)),    args = [args l]; end
if(~isempty(qrl)),  args = [args qrl]; end
if(~isempty(qru)),  args = [args qru]; end

if(~isempty(args))
    wgdx(name,args{:});
end


%% Example of using KST class for interfacing with KUKA iiwa robots

% Using the gamepad for controlling the joints positions of KUKA
% iiwa 7 R 800 robot.
% This script implements a graphical user interface, which gives a feedback
% about the position of the joints' angles.

% This example works with Sunrise application version KST_1.7  and higher.
% Copyright: Mohammad SAFEEA, 29th of June 2018

close all;clear;clc;
warning('off')
% Start the joystick, make sure that the joystick is connected
instrreset;
ID=1;
joy=vrjoystick(ID);
%% Add path of KST class to work space
cDir = pwd;
cDir=getTheKSTDirectory(cDir);
addpath(cDir);

%% Instantiate the KST object
ip='172.31.1.147'; % The IP of the controller
arg1=KST.LBR7R800; % choose the robot iiwa7R800 or iiwa14R820
arg2=KST.Medien_Flansch_elektrisch; % choose the type of flange
Tef_flange=eye(4); % transofrm matrix of EEF with respect to flange
iiwa=KST(ip,arg1,arg2,Tef_flange); % create the object

%% Start a connection with the server
flag=iiwa.net_establishConnection();
if flag==0
	fprintf('Can not connect to KST \n');
    fprintf('Program terminated \n');
    return;
end

%% Move robot to some initial position
jPos={0,0,0,0,0,0,0};
relVel=0.15;
iiwa.movePTPJointSpace(jPos, relVel);

%% Draw the interface
[figureHandle,anglesTextHandlesCellArray,labelTextHandlesCellArray]=constructInterface();

%% max angular velocity
w=10*pi/180; % rad/sec

%% Joint space control
firstExecution=0;
jointIndex=1; % the index of the selcted joint for realtime control.

setBackGroundColor(labelTextHandlesCellArray, 1 );
pause(0.1);
frameUpdateCount=0;
maxDisp=1*pi/180;
while true
    joyStatus=read(joy);
    %% Remove the bias in the analog signal
    analogPrecission=1/50;
    for i=1:4
        if abs( joyStatus(i))<analogPrecission
            joyStatus(i)=0;
        end
    end

    % About the variable (joyStatus)
    % joyStatus: is a 4x1 vector, the first and the second elements of this
    % vector correspond to the analog values of the left analog-stick
    % psoition. the third and the fourth elements of this
    % vector correspond to the analog values of the right analog-stick
    % psoition.
    %
    % Only the seconds and the third elements of this vector are used for
    % controlling the joints motion of the robot one at a time.
    %
    % The 2d element is used to select the robot axes, this element corresponds
    % to the (up,down) directions of the left analog stick of the gamepad.
    %
    % The 3rd element is used to control the motion of the selcted axes,
    % this element corresponds to the (right, left) directions of the right
    % analog stick of the gamepad.

    %% Calculate the elapsed time
    if(firstExecution==0)
        firstExecution=1;
        a=datevec(now);
        timeNow=a(6)+a(5)*60+a(4)*60*60; % calculate time at this instant
        dt=0; % elapsed is zero at first excution
        time0=timeNow;
    else
        a=datevec(now);
        timeNow=a(6)+a(5)*60+a(4)*60*60; % calculate time at this instant
        dt=timeNow-time0; % calculate elapsed time interval
        time0=timeNow;
    end

%% Change index  of controlled axes,
% This happens when the left joiystic of the gamepad is moved in up/down
% direction.
%
    if(abs(joyStatus(2))>0.9)
        step=sign(joyStatus(2))*1;
        jointIndex=jointIndex+step;

        if(jointIndex==0)
            jointIndex=7;
        end

        if(jointIndex==8)
            jointIndex=1;
        end

        setBackGroundColor(labelTextHandlesCellArray, jointIndex );
        pause(0.5);
        continue;
    end

 %% Control axes motion
     if(joyStatus(2)==0)
        % move the axes only if the analog signal corresponding to change
        % axes command is zero.
        dq=w*dt*joyStatus(3);
        %% Check for joint limit avoidance
        dq=dq*jointLimitAvoidance(jointIndex,jPos,dq);
        if dq>maxDisp
            dq=maxDisp;
        end
        jPos{jointIndex}=jPos{jointIndex}+dq;
        frameUpdateCount=frameUpdateCount+1;
        if(frameUpdateCount==50)
            setJointAngleToText(anglesTextHandlesCellArray, jointIndex,jPos{jointIndex} )
            frameUpdateCount=0;
            pause(0.001);
        end
        iiwa.sendJointsPositions(jPos);
     end

     %% A condition to break the loop, when x button of the gamepad is pressed
     b=button(joy,1);
     if(b==1)
         break;
     end

end
close(figureHandle);
%% turn off the server
iiwa.net_turnOffServer();


function [chunk,p] = onl_filtered(p,desired_length,suppress_output,set_online_scope)
% Obtain processed data from a filter pipeline online.
% [Chunk,Pipeline] = onl_filtered(Pipeline,DesiredLength,SuppressOutput,SetOnlineScope)
%
% This function returns a chunk of most recent filtered output from a filter pipeline data structure
% previously created with onl_newpipeline.
%
% A filter pipeline is a recursive data structure (like a tree) whose nodes are the filter stages and
% whose edges represent the output of one stage going into the input of another stage. The leaf
% nodes refer to raw online streams (structs in the workspace) and are queried via onl_peek, all other
% nodes are evaluated by calling the filter function on its input data (recursively).
%
% In:
%   Pipeline : filter pipeline struct to update and to read data from.
%
%   DesiredLength : number of samples to get (or 0 to get all new samples) (default: 0)
%
%   SuppressOutput : suppress console output (default: true)
%
%   SetOnlineScope : set the regular online-processing scope (can be turned off for efficiency if
%                    that scope is already set for some reason) (default: true)
%
% Out:
%   Chunk : EEGLAB dataset struct representing the desired data chunk
%           Can be shorter than desired length if not enough data is available at the moment; if the
%           chunk is epoched, the desired length is ignored.
%
%   Pipeline : updated filter pipeline struct
%
%
% Example:
%   % load calibration set
%   raw = io_loadset('calib.set')
%
%   % apply a series of filter to it (the processed set now has a filter expression and initial state)
%   processed = exp_eval(flt_iir(flt_resample(raw,128),[0.5 1],'highpass'));
%
%   % start streaming some data
%   run_readdataset('mystream','action.set');
%   % and put a pipeline on top of it that replicates the processing applied to processed and continues it on new data
%   pip = onl_newpipeline(processed,{'mystream'});
%
%   while 1
%      % generate a 200-sample view into the processed stream
%      [EEG,pip] = onl_filtered(pip,200);
%   end
%
% See also:
%   onl_newpipeline, onl_newstream, onl_append, onl_peek
%
%                                Christian Kothe, Swartz Center for Computational Neuroscience, UCSD
%                                2012-05-13

% check inputs
if nargin < 4
    set_online_scope = true;
    if nargin < 1
        error('You need to pass a pipeline structure to onl_predict.'); end
    if nargin < 2
        desired_length = 0;
    elseif ~(isnumeric(desired_length) && isscalar(desired_length)) || islogical(desired_length)
        error('The given DesiredLength argument must be a numeric scalar, but was: %s',hlp_tostring(desired_length,10000));
    end
    if nargin < 3
        suppress_output = true;
    elseif ~islogical(suppress_output)
        error('The given SuppressOutput argument must be logical (true or false), but was: %s',hlp_tostring(suppress_output,10000));
    end
else
    if ~(isnumeric(desired_length) && isscalar(desired_length)) || islogical(desired_length)
        error('The given DesiredLength argument must be a numeric scalar, but was: %s',hlp_tostring(desired_length,10000)); end
    if ~islogical(suppress_output)
        error('The given SuppressOutput argument must be logical (true or false), but was: %s',hlp_tostring(suppress_output,10000)); end
    if ~islogical(set_online_scope)
        error('The given SetOnlineScope argument must be logical (true or false), but was: %s',hlp_tostring(set_online_scope,10000)); end
end

if ~all(isfield(p,{'head','parts','subnodes'}))
    error('The given Pipeline argument is not a valid filter pipeline (must be a struct with fields .head, .parts and .subnodes).'); end

% run update_pipeline() with appropriate options
if suppress_output
    if set_online_scope
        [console_output,chunk,p] = evalc('hlp_scope({''disable_expressions'',1,''is_online'',1},@update_pipeline,p)'); %#ok<ASGLU>
    else
        [console_output,chunk,p] = evalc('update_pipeline(p)'); %#ok<ASGLU>
    end
else
    if set_online_scope
        [chunk,p] = hlp_scope({'disable_expressions',1,'is_online',1},@update_pipeline,p);
    else
        [chunk,p] = update_pipeline(p);
    end
end

% if a desired length was specified and the chunk is not epoched
if desired_length && ~isstruct(chunk.epoch)
    try
        % concatenate the chunk with previous buffer contents and trim to desired size
        p.buffer = utl_buffer(chunk,p.buffer,desired_length);
    catch e
        % handle missing .buffer field (first-time use)
        if ~isfield(p,'buffer')
            p.buffer = utl_buffer(chunk,struct('data',[],'event',[]),desired_length);
        else
            rethrow(e);
        end
    end
    chunk = p.buffer;
end

function [chunk,p] = update_pipeline(p)
% Update the given filter pipeline and get a chunk of the newly appended output
% [Chunk,Pipeline] = update_pipeline(Pipeline)
%
% A pipeline is a recursive data structure (like a tree) whose nodes are the filter stages and
% whose edges represent the output of one stage going into the input of another stage. The leaf
% nodes refer to raw data streams (structs in the workspace) and are queried via onl_peek, all other
% nodes are evaluated by calling the filter function on its input data (recursively).
%
% At each node we store the filter function (.head) and its arguments (.parts), some of which may be
% input filter pipelines themselves, plus some miscellaneous book-keeping data. These include:
%  * .israw : true for nodes that represent raw data
%  * .pipeline_indices : indices of those input arguments that are pipelines themselves (if not raw)
%  * .stateful : true if the node has state
%  * .state : previous filter state, if stateful
%  * .smax : number of samples seen so far (if israw)
%
% In:
%   Pipeline : previous filter pipeline struct
%
% Out:
%   Chunk : EEGLAB dataset struct representing newly appended data, filtered
%
%   Pipeline : updated filter pipeline struct

inputs = p.parts;
if p.subnodes
    % update input pipelines to the current node and store the results in inputs
    for k=p.subnodes
        [inputs{k},p.parts{k}] = update_pipeline(p.parts{k}); end
    % process the inputs by calling the respective filter function
    if p.stateful
        [chunk,p.state] = p.head(inputs{:},'state',p.state,'arg_direct',true);
    else
        chunk = p.head(inputs{:});
    end
else
    % get the most recent samples since our buffer's smax from a raw stream:
    % inputs holds the cell array {stream_name,channel_range)
    chunk = onl_peek(inputs{1},p.smax,'index',inputs{2});
    p.smax = chunk.smax;
end


function pageview(mode,cur_axs)
% PAGEVIEW View menu.
%          PAGEVIEW handles all the axis limit altering options under the
%          View pulldown menu.

% Author: Craig Borghesani
% Date: 8/10/94
% Revised: 10/18/94
% Copyright (c) 1999, Prentice-Hall

% obtain handle information
f = gcf;
ui_data = get(f,'userdata');
ui_han = ui_data{1};
last_lims = ui_han(36);

stat_bar = get(ui_han(43),'userdata');

if nargin == 1,
 cur_axs = get(ui_han(32),'userdata');
end

axs_data = get(cur_axs,'userdata');
axs_typ = axs_data(1);

if mode == 1, % Full

 if nargin == 1, set(stat_bar,'string','Setting axis limits to Full'); end

 cur_xlim = get(cur_axs,'xlim');
 cur_ylim = get(cur_axs,'ylim');
 set(last_lims,'userdata',[cur_axs,cur_xlim,cur_ylim]);

 lins = get(cur_axs,'children');
 cur_xlabel = get(cur_axs,'xlabel');
 cur_ylabel = get(cur_axs,'ylabel');
 cur_title = get(cur_axs,'title');
 lims = [1/eps,-1/eps,1/eps,-1/eps];
 for k = 1:length(lins),
  if strcmp(get(lins(k),'vis'),'on'),
   if strcmp(get(lins(k),'type'),'line'),
    xdata = get(lins(k),'xdata');  ydata = get(lins(k),'ydata');
   elseif ~any(lins(k) == [cur_xlabel,cur_ylabel,cur_title]),
    locdata = get(lins(k),'pos');
    xdata = locdata(1); ydata = locdata(2);
   else
    xdata = []; ydata = [];
   end
   if length(xdata) & (abs(xdata(1))<1000 & abs(ydata(1))<1000 & axs_typ==5) | axs_typ~=5,
    lims(1) = min([xdata(~isnan(xdata)),lims(1)]);
    lims(2) = max([xdata(~isnan(xdata)),lims(2)]);
    lims(3) = min([ydata(~isnan(ydata)),lims(3)]);
    lims(4) = max([ydata(~isnan(ydata)),lims(4)]);
   end
  end
 end

 if (lims(1) == 1/eps) | (lims(2) == -1/eps) | all(lims(1:2)==0),
  if any(axs_typ == [1,5]), lims(1) = -1; lims(2) = 1;
  elseif any(axs_typ==[2,3,6,7]), lims(1) = 0.01; lims(2) = 1000;
  elseif axs_typ == 4, lims(1) = -360; lims(2) = 0;
  elseif axs_typ == 8, lims(1) = 0; lims(2) = 10; end
 end

 if (lims(3) == 1/eps) | (lims(4) == -1/eps) | all(lims(3:4)==0),
  if any(axs_typ == [1,5]), lims(3) = -1; lims(4) = 1;
  elseif any(axs_typ == [2,4]), lims(3) = -20; lims(4) = 20;
  elseif axs_typ == 3, lims(3) = -360; lims(4) = 0;
  elseif axs_typ == 6, lims(3) = 0.1; lims(4) = 10;
  elseif axs_typ == 7, lims(3) = 0; lims(4) = 360;
  elseif axs_typ == 8, lims(3) = 0; lims(4) = 5; end
 end

 if any(axs_typ==[1,4,5]),
  if diff(lims(1:2))==0, lims(1:2) = [-abs(lims(1)),abs(lims(1))]; end
  if diff(lims(3:4))==0, lims(3:4) = [-abs(lims(3)),abs(lims(3))]; end
  lims(1:2) = lims(1:2) + diff(lims(1:2))*[-0.1,0.1];
  lims(3:4) = lims(3:4) + diff(lims(3:4))*[-0.1,0.1];
 elseif any(axs_typ == [2,3,7,8]),
  if diff(lims(3:4))==0, lims(3:4) = [lims(3)-10,lims(3)+10]; end
  lims(3:4) = lims(3:4) + diff(lims(3:4))*[-0.1,0.1];
 else
  loglims = log10(lims(3:4));
  loglims = loglims + diff(loglims)*[-0.1,0.1];
  lims(3:4) = 10 .^(loglims);
 end

 if axs_typ == 3,
  if lims(3) < -360, lims(3) = -360; end
  if lims(4) > 0, lims(4) = 0; end
 elseif axs_typ == 4,
  if lims(1) < -360, lims(1) = -360; end
  if lims(2) > 0, lims(2) = 0; end
 elseif axs_typ == 5,
  if lims(1) > 0, lims(1) = -1; end
  if lims(2) < 0, lims(2) = 1; end
 end

 if any(axs_typ == [1, 5]),
  set(cur_axs,{'xlim','ylim'},ctsqaxes(cur_axs,lims(1:2),lims(3:4)));
 else
  set(cur_axs,'xlim',lims(1:2),'ylim',lims(3:4));
 end

elseif any(mode == [2,3]), % In/Out

 if mode == 2,
  out = 0.15;
  if nargin == 1, set(stat_bar,'string','Zooming In'); end
 else
  out = -0.15;
  if nargin == 1, set(stat_bar,'string','Zooming Out'); end
 end

 cur_xlim = get(cur_axs,'xlim');
 cur_ylim = get(cur_axs,'ylim');
 set(last_lims,'userdata',[cur_axs,cur_xlim,cur_ylim]);

 if any(axs_typ == [1,4,5,8]),
  delx = diff(cur_xlim);
  new_xlim = cur_xlim+out*[delx,-delx];
  dely = diff(cur_ylim);
  new_ylim = cur_ylim+out*[dely,-dely];
 elseif any(axs_typ == [2,3,7]),
  loglims = log10(cur_xlim);
  delx = diff(loglims);
  loglims = loglims + out*[delx,-delx];
  new_xlim = 10 .^(loglims);
  dely = diff(cur_ylim);
  new_ylim = cur_ylim+out*[dely,-dely];
 else
  loglims = log10(cur_xlim);
  delx = diff(loglims);
  loglims = loglims + out*[delx,-delx];
  new_xlim = 10 .^(loglims);
  loglims = log10(cur_ylim);
  dely = diff(loglims);
  loglims = loglims + out*[dely,-dely];
  new_ylim = 10 .^(loglims);
 end

 if axs_typ == 3,
  if new_ylim(1) < -360, new_ylim(1) = -360; end
  if new_ylim(2) > 0, new_ylim(2) = 0; end
 elseif axs_typ == 4,
  if new_xlim(1) < -360, new_xlim(1) = -360; end
  if new_xlim(2) > 0, new_xlim(2) = 0; end
 end

 if any(axs_typ == [1, 5]),
  set(cur_axs,{'xlim','ylim'},ctsqaxes(cur_axs,new_xlim,new_ylim));
 else
  set(cur_axs,'xlim',new_xlim,'ylim',new_ylim);
 end

elseif mode == 4, % Zoom

 set(stat_bar,'string','Zoom Mode: press and drag mouse to define new limits');
 set(f,'pointer','crosshair');
 set(f,'windowbuttondownfcn','pageview(10)','windowbuttonupfcn','1;');

elseif mode == 10, % Zoom (RBBOX stage)

 cur_obj = get(f,'currentobject');
 par_cur = get(cur_obj,'parent');
 sel_axs = 0;
 if par_cur == f, % if the parent is the figure, we have an axis object
  sel_axs = cur_obj;
 elseif par_cur,  % if the parent is not the root, then it must be an axis
  sel_axs = par_cur;
 end

 if sel_axs,

  set(stat_bar,'string','Selecting region...');
  pt0 = get(sel_axs,'currentpoint');
  rbbox([get(f,'currentpoint'),0,0],get(f,'currentpoint'));
  drawnow;

  set(stat_bar,'string','Setting new axis limits');
  pt1 = get(sel_axs,'currentpoint');

  cur_xlim = get(sel_axs,'xlim');
  cur_ylim = get(sel_axs,'ylim');
  set(last_lims,'userdata',[sel_axs,cur_xlim,cur_ylim]);

  new_xlim = [min(pt0(1,1),pt1(1,1)), max(pt0(1,1),pt1(1,1))];
  new_ylim = [min(pt0(1,2),pt1(1,2)), max(pt0(1,2),pt1(1,2))];

  if axs_typ == 3,
   if new_ylim(1) < -360, new_ylim(1) = -360; end
   if new_ylim(2) > 0, new_ylim(2) = 0; end
  elseif axs_typ == 4,
   if new_xlim(1) < -360, new_xlim(1) = -360; end
   if new_xlim(2) > 0, new_xlim(2) = 0; end
  end

  set(f,'windowbuttondownfcn','pagemous','windowbuttonupfcn','',...
        'pointer','arrow');

  axs_data = get(sel_axs,'userdata');
  axs_typ = axs_data(1);
  if any(axs_typ == [1, 5]),
   set(sel_axs,{'xlim','ylim'},ctsqaxes(sel_axs,new_xlim,new_ylim));
  else
   set(sel_axs,'xlim',new_xlim,'ylim',new_ylim);
  end

 else

  set(stat_bar,'string','Need to be in an axis region to use Zoom');

 end

elseif mode == 5, % Last

 data = get(last_lims,'userdata');
 set(stat_bar,'string','Restoring Last axis limits');

 cur_xlim = get(cur_axs,'xlim');
 cur_ylim = get(cur_axs,'ylim');
 set(last_lims,'userdata',[cur_axs,cur_xlim,cur_ylim]);

 axs_data = get(data(1),'userdata');
 axs_typ = axs_data(1);
 if any(axs_typ == [1, 5]),
  set(data(1),{'xlim','ylim'},ctsqaxes(cur_axs,cur_xlim,cur_ylim));
 else
  set(data(1),'xlim',cur_xlim,'ylim',cur_ylim);
 end

elseif mode == 6, % grid on/off

 if strcmp(get(cur_axs,'xgrid'),'on'),
  set(stat_bar,'string','Turning grid Off');
  set(cur_axs,'xgrid','off','ygrid','off');
 else
  set(stat_bar,'string','Turning grid On');
  set(cur_axs,'xgrid','on','ygrid','on');
 end

end


function varargout = airy(varargin)

switch class(varargin{1})

    case 'sdpvar'
        varargout{1} = InstantiateElementWise(mfilename,varargin{:});

    case 'char'

        operator = CreateBasicOperator('callback');
        operator.derivative = @(x)(airy(1,x));
        operator.range = [-4.1901547803e-01 5.35656656014e-01];

        varargout{1} = [];
        varargout{2} = operator;
        varargout{3} = varargin{3};

    otherwise
        error(['SDPVAR/' upper(mfilename) ' called with weird argument']);
end


conf = config;

load([conf.root_path, 'splits.mat'], 'train_images', 'val_images');

% check if all images are readible
%check_images(conf.image_path, train_images);

training_images = load_cropped_images(conf.image_path, train_images, ...
    conf.preprocessing.crop_padding, conf.preprocessing.square_size);

savefast(fullfile(conf.root_path,...
    'training_images_crop15_square256.mat'), 'training_images');

validation_images = load_cropped_images(conf.image_path, val_images, ...
    conf.preprocessing.crop_padding, conf.preprocessing.square_size);

savefast(fullfile(conf.root_path,...
    'validation_images_crop15_square256.mat'), 'validation_images');


%% Scene Text Detection using CNN
%
% References:
%
% * Minghui Liao, Baoguang Shi, Xiang Bai, Xinggang Wang, Wenyu Liu.
%   "TextBoxes: A Fast Text Detector with a Single Deep Neural Network",
%   AAAI 2017. <https://arxiv.org/abs/1611.06779>
%
% Sources:
%
% * <https://github.com/opencv/opencv_contrib/blob/3.4.1/modules/text/samples/textbox_demo.cpp>
% * <https://github.com/opencv/opencv_contrib/blob/3.4.1/modules/text/samples/deeptextdetection.py>
%

%%
% load model
textSpotter = TextBoxes();

%%
% detect text
im = which('handicapSign.jpg');
assert(~isempty(im), 'specify input image');
img = cv.imread(im, 'Color',true, 'FlipChannels',false);
[bboxes, confs] = textSpotter.detect(img);
fprintf('%d text detections\n', numel(bboxes));

%%
% post-processing
if true
    % non-maximum suppression
    ind = cv.Net.NMSBoxes(bboxes, confs, 0.3, 0.4);
    ind = ind + 1;  % zero to one-based indices
    bboxes = bboxes(ind);
    confs = confs(ind);
else
    % confidence thresholding
    idx = confs > 0.6;
    bboxes = bboxes(idx);
    confs = confs(idx);
end
fprintf('%d text detections after filtering\n', numel(bboxes));

%%
% show results
img = flip(img, 3);
for i=1:numel(bboxes)
    img = insertAnnotation(img, bboxes{i}, sprintf('%.2f', confs(i)), ...
        'Color',[255 0 255], 'TextColor',[255 255 255], 'Thickness',2, 'FontScale',0.3);
    fprintf('text box: [%3d %3d %3d %3d], confidence: %3.0f%%\n', ...
        bboxes{i}, confs(i)*100);
end
imshow(img), title('TextBox Demo')

%%
% Helper functions

function img = insertAnnotation(img, rect, str, varargin)
    % See also: insertObjectAnnotation, insertShape, insertText
    p = inputParser();
    p.addParameter('Alpha', 0.6);
    p.addParameter('Thickness', 1);
    p.addParameter('Color', [255 255 0]);
    p.addParameter('TextColor', [0 0 0]);
    p.addParameter('FontFace', 'HersheySimplex');
    p.addParameter('FontScale', 0.4);
    p.addParameter('AntiAlias', true);
    p.addParameter('Shape', 'rectangle');
    p.parse(varargin{:});
    opts = p.Results;
    opts.Shape = validatestring(opts.Shape, {'rectangle','circle'});
    thick = 1;

    [sz,b] = cv.getTextSize(str, 'Thickness',thick, ...
        'FontFace',opts.FontFace, 'FontScale',opts.FontScale);
    txt_rect = [rect(1), rect(2)-sz(2)-b, sz(1), sz(2)+b];
    txt_orig = [rect(1), rect(2)-b];

    if opts.AntiAlias
        alias = {'LineType','AA'};
    else
        alias = {'LineType',8};
    end

    overlay = img;
    if strcmp(opts.Shape, 'rectangle')
        overlay = cv.rectangle(overlay, rect, ...
            'Color',opts.Color, 'Thickness',opts.Thickness, alias{:});
    else
        c = rect(1:2) + rect(3:4)/2;
        r = max(rect(3:4)/2);
        overlay = cv.circle(overlay, c, r, ...
            'Color',opts.Color, 'Thickness',opts.Thickness, alias{:});
    end
    overlay = cv.rectangle(overlay, txt_rect, ...
        'Color',opts.Color, 'Thickness','Filled', alias{:});
    if opts.Thickness > 1
        overlay = cv.rectangle(overlay, txt_rect, ...
            'Color',opts.Color, 'Thickness',opts.Thickness, alias{:});
    end
    overlay = cv.putText(overlay, str, txt_orig, ...
        'FontFace',opts.FontFace, 'FontScale',opts.FontScale, ...
        'Color',opts.TextColor, 'Thickness',thick, alias{:});

    img = cv.addWeighted(img,1-opts.Alpha, overlay,opts.Alpha, 0);
end

%%
% Pretrained models

function dname = get_dnn_dir(dname)
    %GET_DNN_DIR  Path to model files, and show where to get them if missing

    dname = fullfile(mexopencv.root(), 'test', 'dnn', dname);
    b = isdir(dname);
    if ~b
        % display help of calling function
        % (assumed to be a local function in current file)
        st = dbstack(1);
        help([mfilename() filemarker() st(1).name])
    end
    assert(b, 'Missing model: %s', dname);
end

function net = TextBoxes()
    %TEXTDETECTOR  Text detector model [Caffe]
    %
    % homepage = https://github.com/MhLiao/TextBoxes
    %
    % ## Model
    %
    % file = test/dnn/TextBoxes/textbox.prototxt
    % url  = https://github.com/opencv/opencv_contrib/raw/3.4.1/modules/text/samples/textbox.prototxt
    % hash = c294416fe6d156b9383342d62b9158ab707170c0
    %
    % ## Weights
    %
    % file = test/dnn/TextBoxes/TextBoxes_icdar13.caffemodel
    % url  = https://www.dropbox.com/s/g8pjzv2de9gty8g/TextBoxes_icdar13.caffemodel?dl=0
    % hash = e4b32aef3db3d66fec0630c96006e10999f785c4
    % size = 90.68 MB
    %

    dname = get_dnn_dir('TextBoxes');
    net = cv.TextDetectorCNN(...
        fullfile(dname, 'textbox.prototxt'), ...
        fullfile(dname, 'TextBoxes_icdar13.caffemodel'), ...
        'DetectionSizes',[300 300]);
end


function [ sicd_meta ] = meta2sicd_palsar2vol( native_meta )
%META2SICD_PALSAR2VOL Converts ALOS PALSAR 2 volume file into a SICD-style metadata structure
%
% Takes as input a metadata structure from read_ceos_vol_meta.
%
% Written by: Wade Schwartzkopf, NGA/R
%
% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////

%% CollectionInfo
if strncmpi(native_meta.vol_set_id, 'ALOS2', 5)
    sicd_meta.CollectionInfo.CollectorName = 'ALOS2';
    sicd_meta.CollectionInfo.RadarMode.ModeID=native_meta.text.prod_id(9:11);
    % Perhaps ScanSAR modes should be DYNAMIC STRIPMAP?
    if strncmpi('SBS',sicd_meta.CollectionInfo.RadarMode.ModeID,3)
        sicd_meta.CollectionInfo.RadarMode.ModeType='SPOTLIGHT';
    else
        sicd_meta.CollectionInfo.RadarMode.ModeType='STRIPMAP';
    end
elseif strncmpi(native_meta.vol_set_id, 'STRIX', 5)
    sicd_meta.CollectionInfo.CollectorName = deblank(native_meta.vol_set_id);
    sicd_meta.CollectionInfo.RadarMode.ModeID=native_meta.text.prod_id(9:10);
    if strncmpi('SM',sicd_meta.CollectionInfo.RadarMode.ModeID,2)
        sicd_meta.CollectionInfo.RadarMode.ModeType='STRIPMAP';
    elseif strncmpi('SL',sicd_meta.CollectionInfo.RadarMode.ModeID,2)
        sicd_meta.CollectionInfo.RadarMode.ModeType='DYNAMIC STRIPMAP';
    end
end
% Do we want to convert CoreName to NGA-style pattern?
sicd_meta.CollectionInfo.CoreName = native_meta.text.scene_id(8:end);
sicd_meta.CollectionInfo.CollectType='MONOSTATIC';
sicd_meta.CollectionInfo.Classification='UNCLASSIFIED';

%% ImageCreation
sicd_meta.ImageCreation.DateTime = ...
    datenum([native_meta.log_vol_create_date ...
             native_meta.log_vol_create_time(1:6)] ,'yyyymmddHHMMSS');
sicd_meta.ImageCreation.Profile='Prototype';

end

% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////


function str= util_toString(var, varargin)
% UTIL_TOSTRING - Convert value of a variable into an (evaluatable) string
%
%Synopsis:
%  STR= util_toString(VAR, <OPT>
%
%Arguments:
%  VAR  -  Variable which is to be converted into a string
%  OPT  -  Struct or property/value list of optional properties:
%    'NumericFormat' [CHAR '%f']  Format string for printing numeric values
%    'MaxDim'        [INT  inf]  Print arrays up to this number of dimensions
%    'MAXNumel'      [INT  inf]  Print arrays up to this number of elements

props= {'NumericFormat'   '%f'    'CHAR'
        'MaxDim'          inf     'INT'
        'MaxNumel'        inf     'INT'
        'inrecursion'     0       'BOOL'
       };

if nargin==0,
  str= props; return
end

opt= opt_proplistToStruct(varargin{:});
opt= opt_setDefaults(opt, props);
if ~opt.inrecursion,
  opt_checkProplist(opt, props);
  opt.inrecursion= 1;
end

if numel(var)>opt.MaxNumel,
  str= sprintf('[%s %s]', str_vec2str(size(var), '%d', 'x'), ...
               upper(class(var)));
  return;
end

str= [];
if islogical(var)
  if prod(size(var))==1
    if var==0, str = 'false'; else str = 'true';end
  elseif isempty(var)
    str = '[]';
  elseif ndims(var)<=2 && size(var,2)==1
    str = sprintf('%s''',util_toString(var', opt));
  elseif (ndims(var)<=2 && size(var,1)==1) || ...
        (ndims(var)<=2 && opt.MaxDim>=2),
    str = '[';
    for i = 1:size(var,1)
      for j = 1:size(var,2)
%        str = [str , util_toString(var(i,j), opt),','];
        str = [str , util_toString(var(i,j), opt),' '];
      end
      str = [str(1:end-1),';'];
    end
    str = [str(1:end-1),']'];
  elseif ndims(var)<=opt.MaxDim,
    str = '[';
    nd = ndims(var);
    sv = size(var); sv(end)=[];
    va = permute(var,[nd,1:nd-1]);
    for i = 1:size(var,nd)
      v = va(i,:)';
      v = reshape(v,sv);
      str = [str util_toString(v, opt) ';'];
    end
    str = [str(1:end-1),']'];
  end
elseif isnumeric(var)
  if prod(size(var))==1
    if var==round(var),
      str = sprintf('%d',var);
    else
      str = sprintf(opt.NumericFormat, var);
    end
  elseif isempty(var)
    str = '[]';
  elseif ndims(var)<=2 && size(var,2)==1
    str = sprintf('%s''',util_toString(var', opt));
  elseif (ndims(var)<=2 && size(var,1)==1) || ...
        (ndims(var)<=2 && opt.MaxDim>=2),
    str = '[';
    for i = 1:size(var,1)
      for j = 1:size(var,2)
%        str = [str , util_toString(var(i,j), opt),','];
        str = [str , util_toString(var(i,j), opt),' '];
      end
      str = [str(1:end-1),';'];
    end
    str = [str(1:end-1),']'];
  elseif ndims(var)<=opt.MaxDim,
    str = '[';
    nd = ndims(var);
    sv = size(var); sv(end)=[];
    va = permute(var,[nd,1:nd-1]);
    for i = 1:size(var,nd)
      v = va(i,:)';
      v = reshape(v,sv);
      str = [str util_toString(v, opt) ';'];
    end
    str = [str(1:end-1),']'];
  end
elseif ischar(var)
  if ndims(var)<=2 & size(var,1)==1
    str = ['''',var,''''];
  elseif isempty(var)
    str = '''''';
  elseif ndims(var)<=2
    str = '[';
    for i = 1:size(var,1)
      str = [str util_toString(var(i,:), opt), ';'];
    end
    str = [str(1:end-1),']'];
  else
    str = '[';
    nd = ndims(var);
    sv = size(var); sv(end)=[];
    va = permute(var,[nd,1:nd-1]);
    for i = 1:size(var,nd)
      v = va(i,:)';
      v = reshape(v,sv);
      str = [str util_toString(v, opt) ';'];
    end
    str = [str(1:end-1),']'];
  end
elseif iscell(var)
   if prod(size(var))==1
    str = ['{',util_toString(var{1}, opt),'}'];
  elseif isempty(var)
    str = '{}';
  elseif ndims(var)<=2 & size(var,2)==1
    str = sprintf('%s''',util_toString(var', opt));
  elseif (ndims(var)<=2 && size(var,1)==1) || ...
        (ndims(var)<=2 && opt.MaxDim>=2),
    str = '{';
    for i = 1:size(var,1)
      for j = 1:size(var,2)
        str = [str , util_toString(var{i,j}, opt),','];
      end
      str = [str(1:end-1),';'];
    end
    str = [str(1:end-1),'}'];
  elseif ndims(var)<=opt.MaxDim,
    str = '{';
    nd = ndims(var);
    sv = size(var); sv(end)=[];
    va = permute(var,[nd,1:nd-1]);
    for i = 1:size(var,nd)
      v = va(i,:)';
      v = reshape(v,sv);
      str = [str util_toString(v, opt) ';'];
    end
    str = [str(1:end-1),'}'];
  end
elseif isstruct(var)
  if prod(size(var)) == 1
    str = 'struct(';
    a = fieldnames(var);
    for i = 1:length(a)
      str = [str, '''', a{i}, ''',', util_toString(getfield(var,a{i}), opt), ...
	     ','];
    end
    str = [str(1:end-~isempty(a)),')'];
  elseif isempty(var)
    str = 'struct([])';
  elseif ndims(var)<=2 && size(var,2)==1
    str = sprintf('%s''',util_toString(var', opt));
  elseif (ndims(var)<=2 && size(var,1)==1) || ...
        (ndims(var)<=2 && opt.MaxDim>=2),
    str = '[';
    for i = 1:size(var,1)
      for j = 1:size(var,2)
        str = [str , util_toString(var(i,j), opt),','];
      end
      str = [str(1:end-1),';'];
    end
    str = [str(1:end-1),']'];
  elseif ndims(var)<=opt.MaxDim,
    str = '[';
    nd = ndims(var);
    sv = size(var); sv(end)=[];
    va = permute(var,[nd,1:nd-1]);
    for i = 1:size(var,nd)
      v = va(i,:)';
      v = reshape(v,sv);
      str = [str util_toString(v, opt) ';'];
    end
    str = [str(1:end-1),']'];
  end
elseif isa(var, 'function_handle'),
  str= ['@' func2str(var)];
end

if isempty(str),
  str= sprintf('[%s %s]', str_vec2str(size(var), '%d', 'x'), ...
               upper(class(var)));
end


function tree_output = eval_cartree(Data,RETree)

%Evaluates the tree output

tree_output = mx_eval_cartree(Data,RETree.nodeCutVar,RETree.nodeCutValue,RETree.childnode,RETree.nodelabel);


function GUI = sessionGUI_plotVoxelData(rois, scans, viewer);
%
%  GUI = sessionGUI_plotVoxelData(<rois>, <scans=scan group>, <viewer>);
%
% Open a Multi Voxel UI(s) for the selected ROIs.
%
% If a viewer is open, will open an MVUI for the currently-selected ROI
% in the selected viewer. If no viewers are open, will open it for the
% selected ROI in the ROI listbox in the session GUI. These are both
% over-ridden if an roi list and/or viewer are manually specified.
%
%
%
% ras, 07/06.
mrGlobals2;

if notDefined('viewer')
    if ~isempty(GUI.viewers)
        viewer = GUI.viewers(GUI.settings.viewer);
    else
        viewer = 0;
    end
end

if notDefined('rois')
    if viewer==0 % no viewer, get from saved list
        rois = guiGet('selectedRois');
    else
        rois = {mrViewGet(viewer, 'selectedRoi')};
    end
end

if ~checkfields(GUI, 'controls', 'mvui')
    GUI.controls.mvui = [];
end

% check if an event-related scan group is assigned.
% If so, use that; otherwise use the GUI selected scans.
if notDefined('scans')
	scans = GUI.settings.scan;
	dt = GUI.settings.dataType;
	if isfield(dataTYPES(dt).scanParams(scans(1)), 'scanGroup') & ...
			~isempty(dataTYPES(dt).scanParams(scans(1)).scanGroup)
		[scans dt] = er_getScanGroup(INPLANE{1});
	end
end

if notDefined('dt')
	dt = guiGet('dataType');
end

for i = 1:length(rois)
    mv = multiVoxelUI(INPLANE{1}, rois{i}, scans, dt);
    GUI.controls.mvui(end+1) = mv.ui.fig;
end

return


function this = subsasgn(this, selector, value)
%SUBSASGN   Modify parameters of CLASSIC+NL optical flow algorithm
%   THIS = SUBSASGN(THIS, SEL, VAL) sets parameter of a HS optical
%   flow algorithm THIS using given selector SEL to value VAL.  The
%   modified object is returned.
%   Available selectors:
%     - ".optical_flow":   Flow base object
%     - ".pyramid_levels": Levels of the image pyramid
%     - ".warping_mode":   'forward' for forward warping and 'backward'
%                          for backward warping
%     - ".solver":         Selector for the linear equation solver
%                          ('backslash' for the builtin solver and 'sor'
%                          for a MEX-based SOR solver)
%     - ".max_iters":      Maximum number of iterations at each pyramid
%                          level
%     - ".limit_update":   Flag whether to restrict the incremental flow
%                          at each level to [-1, 1]
%
% This is a member function of the class 'classic_nl_optical_flow'.
%
% Authors: Deqing Sun, Department of Computer Science, Brown University
% Contact: dqsun@cs.brown.edu
% $Date: $
% $Revision: $
%
% Copyright 2007-2010, Brown University, Providence, RI. USA
%
%                          All Rights Reserved
%
% All commercial use of this software, whether direct or indirect, is
% strictly prohibited including, without limitation, incorporation into in
% a commercial product, use in a commercial service, or production of other
% artifacts for commercial purposes.
%
% Permission to use, copy, modify, and distribute this software and its
% documentation for research purposes is hereby granted without fee,
% provided that the above copyright notice appears in all copies and that
% both that copyright notice and this permission notice appear in
% supporting documentation, and that the name of the author and Brown
% University not be used in advertising or publicity pertaining to
% distribution of the software without specific, written prior permission.
%
% For commercial uses contact the Technology Venture Office of Brown University
%
% THE AUTHOR AND BROWN UNIVERSITY DISCLAIM ALL WARRANTIES WITH REGARD TO
% THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
% FITNESS FOR ANY PARTICULAR PURPOSE.  IN NO EVENT SHALL THE AUTHOR OR
% BROWN UNIVERSITY BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
% DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
% PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
% ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
% THIS SOFTWARE.

  % Allow full access to all fields of the structure
  this = builtin('subsasgn', this, selector, value);


%PRINT2EPS  Prints figures to eps with improved line styles
%
% Examples:
%   print2eps filename
%   print2eps(filename, fig_handle)
%   print2eps(filename, fig_handle, options)
%
% This function saves a figure as an eps file, with two improvements over
% MATLAB's print command. First, it improves the line style, making dashed
% lines more like those on screen and giving grid lines their own dotted
% style. Secondly, it substitutes original font names back into the eps
% file, where these have been changed by MATLAB, for up to 11 different
% fonts.
%
%IN:
%   filename - string containing the name (optionally including full or
%              relative path) of the file the figure is to be saved as. A
%              ".eps" extension is added if not there already. If a path is
%              not specified, the figure is saved in the current directory.
%   fig_handle - The handle of the figure to be saved. Default: gcf.
%   options - Additional parameter strings to be passed to print.

% Copyright (C) Oliver Woodford 2008-2013

% The idea of editing the EPS file to change line styles comes from Jiro
% Doke's FIXPSLINESTYLE (fex id: 17928)
% The idea of changing dash length with line width came from comments on
% fex id: 5743, but the implementation is mine :)

% 14/11/2011: Fix a MATLAB bug rendering black or white text incorrectly.
%             Thanks to Mathieu Morlighem for reporting the issue and
%             obtaining a fix from TMW.
% 08/12/11: Added ability to correct fonts. Several people have requested
%           this at one time or another, and also pointed me to printeps
%           (fex id: 7501), so thank you to them. My implementation (which
%           was not inspired by printeps - I'd already had the idea for my
%           approach) goes slightly further in that it allows multiple
%           fonts to be swapped.
% 14/12/11: Fix bug affecting font names containing spaces. Thanks to David
%           Szwer for reporting the issue.
% 25/01/12: Add a font not to be swapped. Thanks to Anna Rafferty and Adam
%           Jackson for reporting the issue. Also fix a bug whereby using a
%           font alias can lead to another font being swapped in.
% 10/04/12: Make the font swapping case insensitive.
% 26/10/12: Set PaperOrientation to portrait. Thanks to Michael Watts for
%           reporting the issue.
% 26/10/12: Fix issue to do with swapping fonts changing other fonts and
%           sizes we don't want, due to listeners. Thanks to Malcolm Hudson
%           for reporting the issue.
% 22/03/13: Extend font swapping to axes labels. Thanks to Rasmus Ischebeck
%           for reporting the issue.

function print2eps(name, fig, varargin)
options = {'-depsc2'};
if nargin < 2
    fig = gcf;
elseif nargin > 2
    options = [options varargin];
end
% Construct the filename
if numel(name) < 5 || ~strcmpi(name(end-3:end), '.eps')
    name = [name '.eps']; % Add the missing extension
end
% Find all the used fonts in the figure
font_handles = findall(fig, '-property', 'FontName');
fonts = get(font_handles, 'FontName');
if ~iscell(fonts)
    fonts = {fonts};
end
% Map supported font aliases onto the correct name
fontsl = lower(fonts);
for a = 1:numel(fonts)
    f = fontsl{a};
    f(f==' ') = [];
    switch f
        case {'times', 'timesnewroman', 'times-roman'}
            fontsl{a} = 'times-roman';
        case {'arial', 'helvetica'}
            fontsl{a} = 'helvetica';
        case {'newcenturyschoolbook', 'newcenturyschlbk'}
            fontsl{a} = 'newcenturyschlbk';
        otherwise
    end
end
fontslu = unique(fontsl);
% Determine the font swap table
matlab_fonts = {'Helvetica', 'Times-Roman', 'Palatino', 'Bookman', 'Helvetica-Narrow', 'Symbol', ...
                'AvantGarde', 'NewCenturySchlbk', 'Courier', 'ZapfChancery', 'ZapfDingbats'};
matlab_fontsl = lower(matlab_fonts);
require_swap = find(~ismember(fontslu, matlab_fontsl));
unused_fonts = find(~ismember(matlab_fontsl, fontslu));
font_swap = cell(3, min(numel(require_swap), numel(unused_fonts)));
fonts_new = fonts;
for a = 1:size(font_swap, 2)
    font_swap{1,a} = find(strcmp(fontslu{require_swap(a)}, fontsl));
    font_swap{2,a} = matlab_fonts{unused_fonts(a)};
    font_swap{3,a} = fonts{font_swap{1,end}(1)};
    fonts_new(font_swap{1,a}) = {font_swap{2,a}};
end
% Swap the fonts
if ~isempty(font_swap)
    fonts_size = get(font_handles, 'FontSize');
    if iscell(fonts_size)
        fonts_size = cell2mat(fonts_size);
    end
    M = false(size(font_handles));
    % Loop because some changes may not stick first time, due to listeners
    c = 0;
    update = zeros(1000, 1);
    for b = 1:10 % Limit number of loops to avoid infinite loop case
        for a = 1:numel(M)
            M(a) = ~isequal(get(font_handles(a), 'FontName'), fonts_new{a}) || ~isequal(get(font_handles(a), 'FontSize'), fonts_size(a));
            if M(a)
                set(font_handles(a), 'FontName', fonts_new{a}, 'FontSize', fonts_size(a));
                c = c + 1;
                update(c) = a;
            end
        end
        if ~any(M)
            break;
        end
    end
    % Compute the order to revert fonts later, without the need of a loop
    [update, M] = unique(update(1:c));
    [M, M] = sort(M);
    update = reshape(update(M), 1, []);
end
% Set paper size
old_pos_mode = get(fig, 'PaperPositionMode');
old_orientation = get(fig, 'PaperOrientation');
set(fig, 'PaperPositionMode', 'auto', 'PaperOrientation', 'portrait');
% MATLAB bug fix - black and white text can come out inverted sometimes
% Find the white and black text
white_text_handles = findobj(fig, 'Type', 'text');
M = get(white_text_handles, 'Color');
if iscell(M)
    M = cell2mat(M);
end
M = sum(M, 2);
black_text_handles = white_text_handles(M == 0);
white_text_handles = white_text_handles(M == 3);
% Set the font colors slightly off their correct values
set(black_text_handles, 'Color', [0 0 0] + eps);
set(white_text_handles, 'Color', [1 1 1] - eps);
% Print to eps file
print(fig, options{:}, name);
% Reset the font colors
set(black_text_handles, 'Color', [0 0 0]);
set(white_text_handles, 'Color', [1 1 1]);
% Reset paper size
set(fig, 'PaperPositionMode', old_pos_mode, 'PaperOrientation', old_orientation);
% Correct the fonts
if ~isempty(font_swap)
    % Reset the font names in the figure
    for a = update
        set(font_handles(a), 'FontName', fonts{a}, 'FontSize', fonts_size(a));
    end
    % Replace the font names in the eps file
    font_swap = font_swap(2:3,:);
    try
        swap_fonts(name, font_swap{:});
    catch
        warning('swap_fonts() failed. This is usually because the figure contains a large number of patch objects. Consider exporting to a bitmap format in this case.');
        return
    end
end
% Fix the line styles
try
    fix_lines(name);
catch
    warning('fix_lines() failed. This is usually because the figure contains a large number of patch objects. Consider exporting to a bitmap format in this case.');
end
return

function swap_fonts(fname, varargin)
% Read in the file
fh = fopen(fname, 'r');
if fh == -1
    error('File %s not found.', fname);
end
try
    fstrm = fread(fh, '*char')';
catch ex
    fclose(fh);
    rethrow(ex);
end
fclose(fh);

% Replace the font names
for a = 1:2:numel(varargin)
    fstrm = regexprep(fstrm, [varargin{a} '-?[a-zA-Z]*\>'], varargin{a+1}(~isspace(varargin{a+1})));
end

% Write out the updated file
fh = fopen(fname, 'w');
if fh == -1
    error('Unable to open %s for writing.', fname2);
end
try
    fwrite(fh, fstrm, 'char*1');
catch ex
    fclose(fh);
    rethrow(ex);
end
fclose(fh);
return


function ds_info=my_gen_ds_info_gtea(ds_config)

ds_dir=fullfile('../datasets', 'gtea');

train_idx_file=fullfile(ds_dir, 'ImageSets/Segmentation/train.txt');
fid=fopen(train_idx_file);
train_file_names=textscan(fid, '%s');
train_file_names=train_file_names{1};
fclose(fid);

val_idx_file=fullfile(ds_dir, 'ImageSets/Segmentation/val.txt');
fid=fopen(val_idx_file);
val_file_names=textscan(fid, '%s');
val_file_names=val_file_names{1};
fclose(fid);

train_num=length(train_file_names);
img_names=cat(1, train_file_names, val_file_names);
img_num=length(img_names);

img_files=cell(img_num, 1);
mask_files=cell(img_num, 1);

for t_idx=1:img_num
    file_name=img_names{t_idx};
    img_files{t_idx}=[file_name(1:end-4) '.jpg'];
    mask_file_name = strrep(file_name,'JPEGImages','SegmentationClass');
    mask_files{t_idx}=[mask_file_name(1:end-4) '.png'];

end

train_idxes=1:train_num;
val_idxes=train_num+1:img_num;

ds_info=[];

ds_info.img_names=img_names;
ds_info.img_files=img_files;
ds_info.mask_files=mask_files;

ds_info.train_idxes=uint32(train_idxes');
ds_info.test_idxes=uint32(val_idxes');

disp('here::')
img_dir=fullfile(ds_dir, 'JPEGImages')
mask_dir=fullfile(ds_dir, 'SegmentationClass')

data_dirs=[];
data_dirs{1}=img_dir;
data_dirs{2}=mask_dir;

data_dir_idxes_img=zeros([img_num 1], 'uint8')+1;
data_dir_idxes_mask=zeros([img_num 1], 'uint8')+2;

ds_info.data_dir_idxes_img=data_dir_idxes_img;
ds_info.data_dir_idxes_mask=data_dir_idxes_mask;
ds_info.data_dirs=data_dirs;
ds_info.ds_dir=ds_dir;

ds_info.class_info=gen_class_info_ego();

ds_info.ds_name='gtea';

ds_info=process_ds_info_classification(ds_info, ds_config);

end


classdef SteerMathModelTypeEnum < matlab.mixin.SetGet
    %SteerMathModelTypeEnum Summary of this class goes here
    %   Detailed explanation goes here

    enumeration
        GenericPoly('Polynomial');
        SumOfSines('Sum of Sines');
        LinearTangent('Linear Tangent');
        FitNet('Neural Network');
    end

    properties
        name(1,:) char
    end

    methods
        function obj = SteerMathModelTypeEnum(name)
            obj.name = name;
        end
    end

    methods(Static)
        function [listBoxStr, enums] = getListBoxStr()
            m = enumeration('SteerMathModelTypeEnum');
            [~,I] = sort({m.name});
            listBoxStr = {m(I).name};
            enums = m(I);
        end

        function [ind, enum] = getIndForName(name)
            m = enumeration('SteerMathModelTypeEnum');
            [~,I] = sort({m.name});
            m = m(I);
            ind = find(ismember({m.name},name),1,'first');
            enum = m(ind);
        end

        function [enum, ind] = getEnumForListboxStr(nameStr)
            m = enumeration('SteerMathModelTypeEnum');
            ind = find(ismember({m.name},nameStr),1,'first');
            enum = m(ind);
        end
    end
end


function newModel=addMets(model,metsToAdd,copyInfo,prefix)
% addMets
%   Adds metabolites to a model
%
%   model       a model structure
%   metsToAdd   the metabolite structure can have the following fields:
%               mets           cell array with unique strings that
%                              identifies each metabolite (opt, default IDs
%                              of new metabolites are numbered with the
%                              prefix defined below)
%               metNames       cell array with the names of each
%                              metabolite
%               compartments   cell array with the compartment of each
%                              metabolite. Should match model.comps.
%                              If this is a string rather than a cell
%                              array it is assumed that all mets are in
%                              that compartment
%               b              Nx1 or Nx2 matrix with equality constraints
%                              for each metabolite (opt, default 0)
%               unconstrained  vector describing if each metabolite is an
%                              exchange metabolite (1) or not (0) (opt,
%                              default 0)
%               inchis         cell array with InChI strings for each
%                              metabolite (opt, default '')
%               metSmiles      cell array with SMILES strings for each
%                              metabolite (opt, default '')
%               metFormulas    cell array with the formulas for each of
%                              the metabolites (opt, default '')
%               metMiriams     cell array with MIRIAM structures (opt,
%                              default [])
%               metCharges     metabolite charge (opt, default NaN)
%               metNotes       cell array with metabolite notes as strings
%                              (opt, default '')
%   copyInfo    when adding metabolites to a compartment where it previously
%               doesn't exist, the function will copy any available annotation
%               from the metabolite in another compartment (opt, default true)
%   prefix      when metsToAdd.mets is not specified, new metabolite IDs
%               are generated with the prefix specified here. If IDs with
%               the prefix are already used in the model then the
%               numbering will start from the highest existing integer+1
%               (opt, default 'm_')
%
%   newModel     an updated model structure
%
%   NOTE: This function does not make extensive checks about MIRIAM formats,
%   forbidden characters or such.
%
%   Example: If multiple metabolites are added at once, the metMiriams cell
%   array should be defined as (example with ChEBI and KEGG):
%
%   metsToAdd.metMiriams{1} = struct('name',{{'chebi';'kegg.compound'}},...
%       'value',{{'CHEBI:18072';'C11821'}});
%   metsToAdd.metMiriams{2} = struct('name',{{'chebi';'kegg.compound'}},...
%       'value',{{'CHEBI:31132';'C12248'}});
%
%   Usage: newModel=addMets(model,metsToAdd,copyInfo,prefix)

if nargin<3
    copyInfo=true;
end
if nargin<4
    prefix='m_';
else
    prefix=char(prefix);
end

newModel=model;

if isempty(metsToAdd)
    return;
end

%Check some stuff regarding the required fields
if ~isfield(metsToAdd,'mets')
    metsToAdd.mets=generateNewIds(newModel,'mets',prefix,numel(metsToAdd.metNames));
else
    metsToAdd.mets=convertCharArray(metsToAdd.mets);
end
if ~isfield(metsToAdd,'metNames')
    metsToAdd.metNames=metsToAdd.mets;
else
    metsToAdd.metNames=convertCharArray(metsToAdd.metNames);
end
if ~isfield(metsToAdd,'compartments')
    EM='compartments is a required field in metsToAdd';
    dispEM(EM);
else
    metsToAdd.compartments=convertCharArray(metsToAdd.compartments);
    %If only one compartment is given, assume it is for all metabolites
    if numel(metsToAdd.compartments)==1 && numel(metsToAdd.mets)>1
        temp=cell(numel(metsToAdd.mets),1);
        temp(:)=metsToAdd.compartments;
        metsToAdd.compartments=temp;
    end
end

%Number of metabolites
nMets=numel(metsToAdd.mets);
nOldMets=numel(model.mets);
filler=cell(nMets,1);
filler(:)={''};
largeFiller=cell(nOldMets,1);
largeFiller(:)={''};

%Check that no metabolite ids are already present in the model
I=ismember(metsToAdd.mets,model.mets);
if any(I)
    error('One or more elements in metsToAdd.mets are already present in model.mets: %s',...
        strjoin(metsToAdd.mets(I),', '));
else
    newModel.mets=[newModel.mets;metsToAdd.mets(:)];
end

%Check that all the compartments could be found
[I, compMap]=ismember(metsToAdd.compartments,model.comps);
if ~all(I)
    EM='metsToAdd.compartments must match model.comps';
    dispEM(EM);
end

%Check that the metabolite names aren't present in the same compartment.
%Not the neatest way maybe..
t1=strcat(metsToAdd.metNames(:),'***',metsToAdd.compartments(:));
t2=strcat(model.metNames,'***',model.comps(model.metComps));
if any(ismember(t1,t2))
    EM='One or more elements in metsToAdd.metNames already exist in the same compartments as the one it is being added to';
    dispEM(EM);
end

%Some more checks and if they pass then add each field to the structure
if numel(metsToAdd.metNames)~=nMets
    EM='metsToAdd.metNames must have the same number of elements as metsToAdd.mets';
    dispEM(EM);
else
    newModel.metNames=[newModel.metNames;metsToAdd.metNames(:)];
end

if numel(compMap)~=nMets
    EM='metsToAdd.compartments must have the same number of elements as metsToAdd.mets';
    dispEM(EM);
else
    newModel.metComps=[newModel.metComps;compMap(:)];
end

if isfield(metsToAdd,'b')
    if size(metsToAdd.b,1)~=nMets
        EM='metsToAdd.b must have the same number of elements as metsToAdd.mets';
        dispEM(EM);
    else
        %Add empty field if it doesn't exist
        if ~isfield(newModel,'b')
            newModel.b=zeros(nOldMets,1);
        end

        %If the original is only one vector
        if size(metsToAdd.b,2)>size(newModel.b,2)
            newModel.b=[newModel.b newModel.b];
        end
        %Add the new ones
        newModel.b=[newModel.b;metsToAdd.b];
    end
else
    if isfield(newModel,'b')
        %Add the default
        newModel.b=[newModel.b;zeros(nMets,size(newModel.b,2))];
    end
end

if isfield(metsToAdd,'unconstrained')
    if numel(metsToAdd.unconstrained)~=nMets
        EM='metsToAdd.unconstrained must have the same number of elements as metsToAdd.mets';
        dispEM(EM);
    else
        %Add empty field if it doesn't exist
        if ~isfield(newModel,'unconstrained')
            newModel.unconstrained=zeros(nOldMets,1);
        end

        %Add the new ones
        newModel.unconstrained=[newModel.unconstrained;metsToAdd.unconstrained(:)];
    end
else
    if isfield(newModel,'unconstrained')
        %Add the default
        newModel.unconstrained=[newModel.unconstrained;zeros(nMets,1)];
    end
end

if isfield(metsToAdd,'inchis')
    metsToAdd.inchis=convertCharArray(metsToAdd.inchis);
    if numel(metsToAdd.inchis)~=nMets
        EM='metsToAdd.inchis must have the same number of elements as metsToAdd.mets';
        dispEM(EM);
    end
    %Add empty field if it doesn't exist
    if ~isfield(newModel,'inchis')
        newModel.inchis=largeFiller;
    end
    newModel.inchis=[newModel.inchis;metsToAdd.inchis(:)];
else
    %Add empty strings if structure is in model
    if isfield(newModel,'inchis')
        newModel.inchis=[newModel.inchis;filler];
    end
end

if isfield(metsToAdd,'metSmiles')
    metsToAdd.metSmiles=convertCharArray(metsToAdd.metSmiles);
    if numel(metsToAdd.metSmiles)~=nMets
        EM='metsToAdd.metSmiles must have the same number of elements as metsToAdd.mets';
        dispEM(EM);
    end
    %Add empty field if it doesn't exist
    if ~isfield(newModel,'metSmiles')
        newModel.metSmiles=largeFiller;
    end
    newModel.metSmiles=[newModel.metSmiles;metsToAdd.metSmiles(:)];
else
    %Add empty strings if structure is in model
    if isfield(newModel,'metSmiles')
        newModel.metSmiles=[newModel.metSmiles;filler];
    end
end

if isfield(metsToAdd,'metFormulas')
    metsToAdd.metFormulas=convertCharArray(metsToAdd.metFormulas);
    if numel(metsToAdd.metFormulas)~=nMets
        EM='metsToAdd.metFormulas must have the same number of elements as metsToAdd.mets';
        dispEM(EM);
    end
    %Add empty field if it doesn't exist
    if ~isfield(newModel,'metFormulas')
        newModel.metFormulas=largeFiller;
    end
    newModel.metFormulas=[newModel.metFormulas;metsToAdd.metFormulas(:)];
else
    %Add default
    if isfield(newModel,'metFormulas')
        newModel.metFormulas=[newModel.metFormulas;filler];
    end
end

if isfield(metsToAdd,'metCharges')
    if numel(metsToAdd.metCharges)~=nMets
        EM='metsToAdd.metCharges must have the same number of elements as metsToAdd.mets';
        dispEM(EM);
    end
    if ~isnumeric(metsToAdd.metCharges)
        EM='metsToAdd.metCharges must be of type "double"';
        dispEM(EM);
    end
    if ~isfield(newModel,'metCharges')
        newModel.metCharges=NaN(numel(largeFiller),1);
    end
    newModel.metCharges=[newModel.metCharges;metsToAdd.metCharges(:)];
else
    %Add default
    if isfield(newModel,'metCharges')
        newModel.metCharges=[newModel.metCharges;NaN(numel(filler),1)];
    end
end

if isfield(metsToAdd,'metNotes')
    metsToAdd.metNotes=convertCharArray(metsToAdd.metNotes);
    if numel(metsToAdd.metNotes)==1 && numel(metsToAdd.mets)>1
        temp=cell(numel(metsToAdd.mets),1);
        temp(:)=metsToAdd.metNotes;
        metsToAdd.metNotes=temp;
    end
    if numel(metsToAdd.metNotes)~=nMets
        EM='metsToAdd.metNotes must have the same number of elements as metsToAdd.mets';
        dispEM(EM);
    end
    %Add empty field if it doesn't exist
    if ~isfield(newModel,'metNotes')
        newModel.metNotes=largeFiller;
    end
    newModel.metNotes=[newModel.metNotes;metsToAdd.metNotes(:)];
else
    %Add empty strings if structure is in model
    if isfield(newModel,'metNotes')
        newModel.metNotes=[newModel.metNotes;filler];
    end
end

%Don't check the type of metMiriams
if isfield(metsToAdd,'metMiriams')
    if numel(metsToAdd.metMiriams)==1 && numel(metsToAdd.mets)>1
        temp=cell(numel(metsToAdd.mets),1);
        temp(:)={metsToAdd.metMiriams};
        metsToAdd.metMiriams=temp;
    end
    if numel(metsToAdd.metMiriams)~=nMets
        EM='metsToAdd.metMiriams must have the same number of elements as metsToAdd.mets';
        dispEM(EM);
    end
    %Add empty field if it doesn't exist
    if ~isfield(newModel,'metMiriams')
        newModel.metMiriams=cell(nOldMets,1);
    end
    newModel.metMiriams=[newModel.metMiriams;metsToAdd.metMiriams(:)];
else
    if isfield(newModel,'metMiriams')
        newModel.metMiriams=[newModel.metMiriams;cell(nMets,1)];
    end
end

if isfield(newModel,'metFrom')
    newModel.metFrom=[newModel.metFrom;filler];
end

%Expand the S matrix
newModel.S=[newModel.S;sparse(nMets,size(newModel.S,2))];

if copyInfo==true
    [I, J]=ismember(metsToAdd.metNames,model.metNames);
    J=J(I);
    %I is the indexes of the new metabolites for which a metabolite with
    %the same name existed
    I=find(I)+nOldMets;
    %Go through each of the added mets and copy annotation if it doesn't
    %exist
    for i=1:numel(I)
        if isfield(newModel,'inchis')
            if isempty(newModel.inchis{I(i)})
                newModel.inchis(I(i))=newModel.inchis(J(i));
            end
        end
        if isfield(newModel,'metSmiles')
            if isempty(newModel.metSmiles{I(i)})
                newModel.metSmiles(I(i))=newModel.metSmiles(J(i));
            end
        end
        if isfield(newModel,'metFormulas')
            if isempty(newModel.metFormulas{I(i)})
                newModel.metFormulas(I(i))=newModel.metFormulas(J(i));
            end
        end
        if isfield(newModel,'metMiriams')
            if isempty(newModel.metMiriams{I(i)})
                newModel.metMiriams(I(i))=newModel.metMiriams(J(i));
            end
        end
        if isfield(newModel,'metCharges')
            newModel.metCharges(I(i))=newModel.metCharges(J(i));
        end
    end
end
end


%Transcriptional Regulation
%
% @wholeCellModelID Process_TranscriptionalRegulation
% @name             Transcriptional Regulation
% @description
%   Biology
%   ==============
%   The the rate of transcription of each transcription unit is known to be
%   regulated by proteins referred to as transcription factors which modulate
%   the affinity of the RNA polymerase for each promoter. Transcription factors
%   can both positively and negatively modulate RNA polymerase - promoter
%   affinity. Transcription factors can stabilize RNA polymerase - promoter
%   complexes by contributing additional negative free energy to the complex by
%   providing an additional surface for the RNA polymerase binding.
%   Transcription factors can destabilize RNA polymerase - promoter complexes
%   for example by sterically blocking promoters and preventing the RNA
%   polymerase from binding promoters.
%
%   This process simulates the binding of transcription factors to the promoters
%   of each transcription unit (affinity), as well as the affect of the
%   transcription factors on the recruiment of the RNA polymerase to each
%   promoter (activity). The process was built using experimentally observed fold
%   change expression effect of each transcription factor on each promoter.
%
%   Affinity
%   ++++++++++++++
%   Binds enzymes to promoters assuming:
%   1) Transcription factors have high affinity for promoters
%   2) Transcription factors bind promoters rapidly
%   3) Transcription factors bind promoters stably over time
%   4) Only 1 copy of a transcription factor can bind each promoter
%   5) Transcription factors only compete within their species for
%      promoters. That is for each transcription unit we assume each
%      transcription factor species binds a distinct promoters region.
%
%   Consequently, at each time step we simulate that each free
%   transcription factor binds randomly binds unoccupied promoters (no copy
%   of that transcription factor is already bound to the promoter). Random
%   transcription factor-promoter binding is weighted by the affinity of
%   each transcription factor for each promoter.
%
%   Because transcription factor-promoter affinities are generally not
%   experimentally observed, we base them on the transcription factor fold
%   change activities.
%
%   Activity
%   ++++++++++++++
%   The effect of bound transcription factors on the recruitment of RNA
%   polymerase and the expression of transcription units is simulated here
%   and incorporated into the calculation of the RNA polymerase
%   transcription unit promoter binding probabilities in the transcription
%   process. Specifically, the wild-type average RNA polymerase
%   transcription unit promoter binding probabilities are multiplied by the
%   binding probability fold change effects simulated in this process.
%
%   The RNA polymerase binding probability fold change is simulated for
%   each promoter as the product of the observed expression fold change
%   effects of each bound transcription factor. When a promoter is bound by
%   a single transcription factor, the net RNA polymerase binding
%   probability fold change is the observed expression fold change of that
%   transcription factor. When a promoter is bound by multiple
%   transcription units, the net RNA polymerase binding probability fold
%   change is given by the product of the individual fold change effects of
%   the bound transcription factors.
%
%   Knowledge Base
%   ==============
%   The list of transcriptional regulatory relationships is maintained in the
%   the knowledge base. As of 8/10/2010, it contained 31 such relationships
%   between 5 transcription factors and 29 transcription units containing 37
%   genes. The knowledge base was built from a variety of literature sources
%   and databases including:
%   - PUB_0096
%   - PUB_0110
%   - PUB_0112
%   - PUB_0196
%   - PUB_0418-20
%   - PUB_0433-8
%   - PUB_0505
%
%   Initialization
%   =================
%   Because we assume that transcription factors have high affinity for DNA and
%   bind DNA stably, we initialize as many transcription factors as possible to
%   the promoter-bound state. We randomly assign transcription factors to
%   promoters using their relative affinities.
%
%   Simulation
%   ==============
%   For each kind of transcription factor, bind any free transcription factors
%   to promoters that aren't already occupied by this kind of transcription
%   factor. Choose the binding sites randomly, weighted by the transcription
%   factor's affinity to them.
%
%   References
%   ==============
%   1) Lacramioara Bintu and Nicolas E Buchler and Hernan G Garcia and
%      Ulrich Gerland and Terence Hwa, Jane Kondev and Rob Phillips (2005).
%      Transcriptional regulation by the numbers: models. Curr Opin Genet
%      Dev. 15:116-24.
%   2) Lacramioara Bintu and Nicolas E Buchler and Hernan G Garcia and
%      Ulrich Gerland and Terence Hwa, Jane Kondev and Rob Phillips (2005).
%      Transcriptional regulation by the numbers: applications. Curr Opin
%      Genet Dev. 15:125-35.
%
% Author: Jonathan Karr, jkarr@stanford.edu
% Author: Jared Jacobs, jmjacobs@stanford.edu
% Affilitation: Covert Lab, Department of Bioengineering, Stanford University
% Last updated: 10/19/2010
classdef TranscriptionalRegulation < edu.stanford.covert.cell.sim.Process & edu.stanford.covert.cell.sim.ChromosomeProcessAspect
    %property annotations
    properties (Constant)
        optionNames__              = {}; %names of option properties
        fixedConstantNames__       = {   %names of fixed constant properties
		    'tfIndexs';
            'tuIndexs';
            'tfAffinities';
            'tfActivities';
            'tfPositionStrands';
            'otherActivities';
		    };
        fittedConstantNames__      = {}; %names of fitted constant properties
        localStateNames__          = {}; %names of simulation state properties redundant with timecourses in this or other processes or the simulation
    end

    %IDs, names, and local indices
    properties
        stimuliWholeCellModelIDs = {};
        substrateWholeCellModelIDs = {};

        enzymeWholeCellModelIDs
        enzymeIndexs_fur         %MG_236_MONOMER  ferric uptake repressor
        enzymeIndexs_gntR        %MG_101_MONOMER  Uncharacterized HTH-type transcriptional regulator
        enzymeIndexs_hrcA        %MG_205_DIMER    heat-inducible transcription repressor HrcA, putative
        enzymeIndexs_luxR        %MG_428_DIMER    LuxR bacterial regulatory protein, putative
        enzymeIndexs_spx         %MG_127_MONOMER  Spx subfamily protein

        transcriptionUnitWholeCellModelIDs
    end

    %fixed biological constants
    properties
        tfIndexs           %identifying index of transcription factor that binds each site [sites X 2]
        tuIndexs           %identifying index of transcription unit associated with each TF binding site [sites X 2]
        tfAffinities       %affinity of each transcription factor for each promoter it binds [sites X 2]
        tfActivities       %fold change expression of each transcription unit when its promoter is bound by a particular transcription factor [sites X 2]
        tfPositionStrands  %start position and strand of each transcription factor DNA binding site [2*sites X 2]
        otherActivities    %fold change each non-DNA-binding transcription factor has on each transcription unit's expression [nTFs X nTUs]
    end

    %dependent local state (implemented as dependent property for convenience)
    properties (Dependent, SetAccess = protected)
        tfBoundPromoters       %number of each transcription factor bound to promoter of each transcription unit [transcription factors X promoters X 2]
        boundTFs               %count of how many of each transcription factor are bound [transcription factors X 1]
    end

    %references to cell state
    properties
        rnaPolymerase             %reference to RNA polymerase state
    end

    %constructor
    methods
        function this = TranscriptionalRegulation(wholeCellModelID, name)
            this = this@edu.stanford.covert.cell.sim.Process(wholeCellModelID, name);
        end
    end

    %communication between process/simulation
    methods
        %set references to state objects
        function storeObjectReferences(this, simulation)
            this.storeObjectReferences@edu.stanford.covert.cell.sim.Process(simulation);
            this.storeObjectReferences@edu.stanford.covert.cell.sim.ChromosomeProcessAspect(simulation);

            this.rnaPolymerase = simulation.state('RNAPolymerase');
            this.states = [this.states; {this.rnaPolymerase}];
        end

        %initialize constants
        function initializeConstants(this, knowledgeBase, varargin)
            %build object arrays of transcription factor monomers and complexes
            monomers = [];
            complexs = [];
            for i = 1:knowledgeBase.numTranscriptionUnits
                m = knowledgeBase.transcriptionUnits(i).transcriptionFactorProteinMonomers;
                c = knowledgeBase.transcriptionUnits(i).transcriptionFactorProteinComplexs;
                if ~isempty(m)
                    monomers = [monomers; m]; %#ok<AGROW>
                end
                if ~isempty(c)
                    complexs = [complexs; c]; %#ok<AGROW>
                end
            end

            %whole cell model ids of all transcription factors
            this.enzymeWholeCellModelIDs = unique({
                monomers.wholeCellModelID...
                complexs.wholeCellModelID}');

            %transcription factors indices
            this.enzymeIndexs_fur  = this.enzymeIndexs({'MG_236_MONOMER'});
            this.enzymeIndexs_gntR = this.enzymeIndexs({'MG_101_MONOMER'});
            this.enzymeIndexs_hrcA = this.enzymeIndexs({'MG_205_DIMER'});
            this.enzymeIndexs_luxR = this.enzymeIndexs({'MG_428_DIMER'});
            this.enzymeIndexs_spx  = this.enzymeIndexs({'MG_127_MONOMER'});

            %call super class method to compute mapping between enzymes of
            %this process, and of protein monomers and complexes of the simulation
            this.initializeConstants@edu.stanford.covert.cell.sim.Process(...
                knowledgeBase, varargin{:});
            this.initializeConstants@edu.stanford.covert.cell.sim.ChromosomeProcessAspect(...
                knowledgeBase, varargin{:});

            %whole cell model ids of transcription units
            this.transcriptionUnitWholeCellModelIDs = ...
                {knowledgeBase.transcriptionUnits.wholeCellModelID}';

            %build matrices of transcription factor positions, affinities, activities
            n = length(monomers) + length(complexs);
            this.tuIndexs = zeros(n, 2);
            this.tfIndexs = zeros(n, 2);
            this.tfActivities = zeros(n, 2);
            this.tfPositionStrands = zeros(n*2, 2);
            c = this.chromosome;
            j = 1;
            for i = 1:knowledgeBase.numTranscriptionUnits
                tu = knowledgeBase.transcriptionUnits(i);

                if ~isempty(tu.transcriptionFactorProteinMonomers)
                    [tf, proteinIndexs] = ismember(...
                        [tu.transcriptionFactorProteinMonomers.idx]',...
                        this.enzymeMonomerGlobalIndexs);
                    k = nnz(tf) - 1;
                    this.tuIndexs(j:j+k) = i;

                    proteinIndexs = proteinIndexs(tf);
                    this.tfIndexs(j:j+k) = this.enzymeMonomerLocalIndexs(proteinIndexs);

                    regulationIndexs = ...
                        this.enzymeMonomerCompartmentIndexs(proteinIndexs) == ...
                        [tu.transcriptionFactorProteinMonomerCompartments.idx]';
                    this.tfActivities(j:j+k) = ...
                        tu.transcriptionFactorProteinMonomerActivitys(regulationIndexs);

                    footprints = c.monomerDNAFootprints([tu.transcriptionFactorProteinMonomers.idx]');
                    startCoordinates = tu.transcriptionFactorProteinMonomerBindingSiteStartCoordinates;
                    lengths = tu.transcriptionFactorProteinMonomerBindingSiteLengths;
                    this.tfPositionStrands(j:j+k) = ...
                        mod(ceil(startCoordinates + lengths/2 - footprints/2) - 1, c.sequenceLen) + 1;
                    j = j + 1 + k;
                end

                if ~isempty(tu.transcriptionFactorProteinComplexs)
                    [tf, proteinIndexs] = ismember(...
                        [tu.transcriptionFactorProteinComplexs.idx]',...
                        this.enzymeComplexGlobalIndexs);
                    k = nnz(tf) - 1;
                    this.tuIndexs(j:j+k) = i;

                    proteinIndexs = proteinIndexs(tf);
                    this.tfIndexs(j:j+k) = this.enzymeComplexLocalIndexs(proteinIndexs);

                    regulationIndexs = ...
                        this.enzymeComplexCompartmentIndexs(proteinIndexs) == ...
                        [tu.transcriptionFactorProteinComplexCompartments.idx]';
                    this.tfActivities(j:j+k) = ...
                        tu.transcriptionFactorProteinComplexActivitys(regulationIndexs);

                    footprints = c.complexDNAFootprints([tu.transcriptionFactorProteinComplexs.idx]');
                    startCoordinates = tu.transcriptionFactorProteinComplexBindingSiteStartCoordinates;
                    lengths = tu.transcriptionFactorProteinComplexBindingSiteLengths;
                    this.tfPositionStrands(j:j+k) = ...
                        mod(ceil(startCoordinates + lengths/2 - footprints/2) - 1, c.sequenceLen) + 1;
                    j = j + 1 + k;
                end
            end

            validateattributes(this.tfActivities, {'numeric'}, {'nonnegative'});

            this.tuIndexs(:,2) = this.tuIndexs(:,1);
            this.tfIndexs(:,2) = this.tfIndexs(:,1);
            this.tfActivities(:,2) = this.tfActivities(:,1);

            this.tfPositionStrands(1:n,2) = 1;
            this.tfPositionStrands(n+1:2*n,1) = this.tfPositionStrands(1:n,1);
            this.tfPositionStrands(n+1:2*n,2) = 3;

            this.tfAffinities = this.tfActivities;
            this.tfActivities(this.tfActivities<1 & this.tfAffinities>0) = ...
                this.tfActivities(this.tfActivities<1 & this.tfAffinities>0) .^ -1;

            bind = ~isnan(this.tfPositionStrands(1:n,1));
            this.otherActivities = ones(...
                length(this.transcriptionUnitWholeCellModelIDs), ...
                length(this.enzymeWholeCellModelIDs));
            this.otherActivities(sub2ind(...
                size(this.otherActivities), ...
                this.tuIndexs(~bind), ...
                this.tfIndexs(~bind))) = ...
                this.tfActivities(~bind);
            this.tuIndexs = this.tuIndexs(bind,:);
            this.tfIndexs = this.tfIndexs(bind,:);
            this.tfAffinities = this.tfAffinities(bind,:);
            this.tfActivities = this.tfActivities(bind,:);
            this.tfPositionStrands = this.tfPositionStrands(repmat(bind, [2 1]), :);
        end
    end

    %model
    methods
        %Calculate
        %- contribution to FBA objective
        %- minimum expression consistent with cell cycle length
        function [bmProd, byProd, minEnzExp, maxEnzExp] = calcResourceRequirements_LifeCycle(this, ~, ~)
            %no substrate and byproducts required
            bmProd = zeros(size(this.substrateWholeCellModelIDs));
            byProd = zeros(size(this.substrateWholeCellModelIDs));

            %no enzymes required
            minEnzExp = zeros(size(this.enzymeWholeCellModelIDs));
            maxEnzExp = Inf(size(this.enzymeWholeCellModelIDs));
        end

        %initialization:
        %- Simulation initializeState method initializes 1 undamaged chromosome
        %- Various processes may bind proteins to chromosome
        %- Here we bind transcription factors to the initial chromosome
        function initializeState(this)
            this.evolveState();
        end

        %resource requirements
        function result = calcResourceRequirements_Current(this)
            result = zeros(size(this.substrates));
        end

        %simulation
        function evolveState(this)
            %Bind transcription factors to accessible sites
            tfBoundPromoters = this.bindTranscriptionFactors();

            %Compute new fold changes
            this.rnaPolymerase.transcriptionFactorBindingProbFoldChange = ...
                this.calcBindingProbabilityFoldChange(tfBoundPromoters);
        end

        %Bind transcription factors to accessible sites.
        function tfBoundPromoters = bindTranscriptionFactors(this)
            %Estimate which binding sites exist and are accessible to the
            %transcription unit. (Note chromosome class which ensure that sites
            %are truly accessible, and only allow transcription factors to bind
            %to these sites).
            tfBoundPromoters = this.tfBoundPromoters;
            accessible = ~tfBoundPromoters & reshape(this.chromosome.isRegionPolymerized(this.tfPositionStrands, 1, false), [], 2);

            for i = 1:size(this.enzymes, 1)
                %For each transcription factor:
                %  Stochastically pick transcription unit promoters elements to
                %  bind from among free promoter elements weighted by the
                %  transcription factors' affinities for the promoters. (Note:
                %  each transcription factor is assumed to bind a distinct
                %  promoter region near each transcription unit such that
                %  transcription factors only compete within their species
                %  for binding to promoters.)

                if this.enzymes(i) <= 0
                    continue;
                end

                sites = find((this.tfIndexs == i) & accessible);
                if isempty(sites)
                    continue;
                end

                tf = this.bindProteinToChromosome(...
                    this.tfPositionStrands(sites, :), i, this.enzymes(i), this.tfAffinities(sites), [], false, 1, false, []);

                tfBoundPromoters(sites(tf)) = true;
            end
        end

        function foldChange = calcBindingProbabilityFoldChange(this, tfBoundPromoters)
            %reset fold change
            foldChange = ones(size(this.chromosome.transcriptionUnitStartCoordinates, 1), 2);

            %fold change effect of bound TFs
            for i = 1:size(this.tfIndexs, 1)
                for j = 1:size(this.tfIndexs, 2)
                    if ~tfBoundPromoters(i, j)
                        continue;
                    end

                    foldChange(this.tuIndexs(i, j), j) = foldChange(this.tuIndexs(i, j), j) * this.tfActivities(i, j);
                end
            end

            otherFoldChanges = prod(this.otherActivities .^ (this.enzymes(:, ones(1, size(foldChange, 1)))' > 0), 2);
            foldChange = foldChange .* [otherFoldChanges otherFoldChanges];
        end
    end

    %get methods of dependent local state
    methods
        function result = get.tfBoundPromoters(this)
            result = reshape(this.isDnaBound(this.tfPositionStrands, this.tfIndexs(:)), [], 2);
        end

        function result = get.boundTFs(this)
            result = histc(this.tfIndexs(logical(this.tfBoundPromoters(:,1))), 1:numel(this.enzymes));
        end
    end
end


function extractLabels(jabfile,outfile)

J = load(jabfile,'-mat');
oo = fopen(outfile,'w');

for exp = 1:numel(J.x.labels)
  for ndx = 1:numel(J.x.labels(exp).t0s)
    for bndx = 1:numel(J.x.labels(exp).t0s{ndx})
      fprintf(oo,'%s %d %d %d %s\n',J.x.expDirNames{exp},...
        J.x.labels(exp).flies(ndx),...
        J.x.labels(exp).t0s{ndx}(bndx),...
        J.x.labels(exp).t1s{ndx}(bndx)-1,...
        J.x.labels(exp).names{ndx}{bndx}...
        );
    end

  end

end
fclose(oo);


function display(X)
%DISPLAY   Display information about a CHEBFUN2.
%   DISPLAY(F) outputs important information about the CHEBFUN2 F to the command
%   window, including its domain of definition, length (number of pivots used to
%   represent it), and a summary of its structure.
%
%   It is called automatically when the semicolon is not used at the end of a
%   statement that results in a CHEBFUN2.
%
% See also DISP.

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

if ( isequal(get(0, 'FormatSpacing'), 'compact') )
	disp([inputname(1), ' =']);
	disp(X);
else
	disp(' ');
	disp([inputname(1), ' =']);
	disp(' ');
    disp(X);
end

end


function obj_cell = split(obj, varargin)
% Split fmri_data object into separate objects with different images in each
% default: Use the obj.images_per_session field
%
% :Usage:
% ::
%
%     obj_cell = split(obj, [optional inputs])
%
% For objects: Type methods(object_name) for a list of special commands
%              Type help object_name.method_name for help on specific
%              methods.
%
% ..
%     Author and copyright information:
%
%     Copyright (C)2016 Tor Wager
%
%     This program is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
%     This program is distributed in the hope that it will be useful,
%     but WITHOUT ANY WARRANTY; without even the implied warranty of
%     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%     GNU General Public License for more details.
%
%     You should have received a copy of the GNU General Public License
%     along with this program.  If not, see <http://www.gnu.org/licenses/>.
% ..
%
% :Inputs:
%
%   **obj:**
%        An fmri_display object with multiple images, to split
%
% :Optional Inputs:
%   **'imgs_per_obj'**
%        Followed by a vector of number of images in each object
%        e.g., for 100 images, [25 25 25 25] would split into 4 objects of
%        25 images each
%        Default: Use obj.images_per_session
%
% :Outputs:
%
%   **objcell:**
%        A cell array with each output fmri_data object in a cell.
%
%
% :Examples:
% ::
%
%
% :References:
%   None.
%
% :See also:
%   - list other functions related to this one, and alternatives*
%

% ..
%    Programmers' notes:
%    List dates and changes here, and author of changes
% ..
% Tor Wager, Aug 2016
% Added  >= en(i) to some lines - bug fix. Seemed to have little effect on most cases. Tor, June 2017

% ..
%    DEFAULTS AND INPUTS
% ..

imgs_per_obj = obj.images_per_session;

% optional inputs with default values
for i = 1:length(varargin)
    if ischar(varargin{i})
        switch varargin{i}

            case 'imgs_per_obj', imgs_per_obj = varargin{i+1}; varargin{i+1} = [];

            otherwise, warning(['Unknown input string option:' varargin{i}]);
        end
    end
end

if isempty(imgs_per_obj)
    obj_cell = {obj};
    return
end

obj = replace_empty(obj);  % to replace removed images

if ~isrow(imgs_per_obj), imgs_per_obj = imgs_per_obj'; end

en = cumsum(imgs_per_obj);
st = [0 en(1:end-1)] + 1;

k = length(imgs_per_obj);
obj_cell = cell(1, k);

% warn user about fields that aren't handled but appear like they should be
% split. Some of these (e.g. additional_info) are handled by
% fmri_data.cat(), so it may surprise the user that split doesn't invert
% the process.
%
% criteria for warning: if length of field is equal to number of splits
fnames = {'image_names', 'fullpath', 'files_exist', 'X', 'Y'};
supportedFields = {fnames{:}, 'dat', 'images_per_session', 'removed_images'};
allFields = fieldnames(obj);

for i = 1:length(allFields)
    if sum(strcmp(allFields{i},supportedFields)) == 0
        if isa(obj.(allFields{i}),'table')
            if height(obj.(allFields{i})) == k
                warning(['Object''s ''', allFields{i}, ''' field length matches number of splits, but ''', allFields{i}, ''' field will not be split'])
            end
        elseif length(obj.(allFields{i})) == k
            warning(['Object''s ''', allFields{i}, ''' field length matches number of splits, but ''', allFields{i}, ''' field will not be split'])
        end
    end

end

for i = 1:k

    obj_cell{i} = obj;

    wh = st(i):en(i);

    obj_cell{i}.dat = obj_cell{i}.dat(:, wh);

    obj_cell{i}.images_per_session = en(i) - st(i) + 1;

    for j=1:length(fnames)

        if ~isempty(obj_cell{i}.(fnames{j})) && size(obj_cell{i}.(fnames{j}), 1) >= en(i)

            obj_cell{i}.(fnames{j}) = obj_cell{i}.(fnames{j})(wh, :);

        end
    end

    obj_cell{i} = check_image_filenames(obj_cell{i}, 'noverbose');

    if ~isempty(obj_cell{i}.removed_images) && size(obj_cell{i}.removed_images, 1) >= en(i)

        obj_cell{i}.removed_images = obj_cell{i}.removed_images(wh, :);

    else

        obj_cell{i}.removed_images = 0; % add in case

    end

end

end % function


function [ readerobj ] = open_tiff_reader(filename)
%OPEN_TIFF_READER Intiates a reader object for TIFF imagery
%
% Works for most generic TIFFs, but really intended for SAR datasets stored
% in TIFF format.  For some GeoTIFFs (like RADARSAT-2 and Sentinel-1) this
% will also find and process metadata into the SICD format.
%
% Written by: Wade Schwartzkopf, NGA/IDT
%
% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////

%% Sensor-specific metadata
% A TIFF file by itself is usually light on any SAR metadata.  Here we look
% for sensor-specific metadata files that might be associated with this
% TIFF file and use them to compute a SICD metadata structure.
tiff_sensor = guess_tiff_sensor(filename);
meta2sicd_tiff_fun = ['meta2sicd_tiff' tiff_sensor];
if ~isempty(tiff_sensor) && exist(meta2sicd_tiff_fun,'file')==2 % Sensor-specific
    [meta, symmetry] = feval(meta2sicd_tiff_fun, filename);
else
    meta = struct();
    symmetry = [0 0 0]; % Default orientation if none can be determined
end

%% Open reader
tiff_meta = imfinfo(filename);
if isfield(tiff_meta,'SampleFormat') && ... % Normal MATLAB TIFF reader does not handle complex data types
        any(strncmpi(tiff_meta.SampleFormat,'complex',7))
    symmetry(3) = ~symmetry(3); % Our TIFF reader is transposed from MATLAB's
    if iscell(meta) % TIFF should be split into multiple parts
        % This case really only works for Sentinel1.  We should make a more
        % generic solution that allows the meta2sicd function to define how
        % TIFF is split.
        last_col = 0;
        base_reader = open_ctiff_reader_noxml(filename,symmetry);
        for i = 1:numel(meta)
            readerobj{i} = subset_reader(base_reader,...
                    last_col + [1 meta{i}.ImageData.NumCols],[1 meta{i}.ImageData.NumRows]);
            last_col = last_col + meta{i}.ImageData.NumCols;
        end
    else
        readerobj = open_ctiff_reader_noxml(filename,symmetry);
    end
else
    readerobj = open_tiff_reader_noxml(filename,symmetry);
end
if iscell(meta)
    for i = 1:numel(meta)
        meta{i} = setstructfields(meta{i},readerobj{i}.get_meta()); % Merge metadata
        readerobj{i}.get_meta=@() meta{i};
    end
else
    meta = setstructfields(meta,readerobj.get_meta()); % Merge metadata
    readerobj.get_meta=@() meta;
end

% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////