 function ob = Gblur(mask, varargin)
%function ob = Gblur(mask, options)
%|
%| Construct Gblur object for image restoration.
%|
%| See Gblur_test.m for example usage.
%|
%| in
%|	mask	size(image)	logical array of object support.
%|
%| options
%|	'chat'		verbose printing of debug messages
%|	'psf'		point spread function (aka impulse response)
%|	'type'		type of blur:
%|				'conv,same'	usual case (default)
%|				'conv,per'	(periodic end conditions)
%|				'conv,valid'	(realistic extended input)
%|				'fft,same'	(periodic end conditions)
%|				todo: allow replicated end conditions!
%|				'imfilter,same'	(requires image toolbox)
%|				'imfilter,circ'	(requires image toolbox)
%|				'imfilter,mirror' (requires image toolbox)
%|					(do not use - adjoint fails)
%|				'sparse'	todo: return sparse matrix
%|	'imfilter_options'	options to 'imfilter' (if used)
%|
%| out
%|	ob [nd np]	np = sum(mask(:)), so it is already "masked"
%|			nd = prod(size(mask)) for 'conv,same' type
%|
%| Copyright 2005-4-22, Jeff Fessler, University of Michigan

if nargin == 1 && streq(mask, 'test'), Gblur_test, return, end
if nargin < 1, ir_usage, end

arg.mask = mask;

% option defaults
arg.chat = 0;
arg.psf = 1; % identity matrix
arg.type = 'conv,same';
arg.class = 'fatrix2';
arg.imfilter_options = {};

% options specified by name/value pairs
arg = vararg_pair(arg, varargin);

if ndims(arg.psf) ~= ndims(mask), error 'psf dim mismatch', end
if any(size(arg.psf) > size(mask)), error 'psf too large', end

if streq(arg.type, 'imfilter,circ') && exist('imfilter') ~= 2
	warn 'no imfilter (no image processing toolbox) so using fft'
	arg.type = 'fft,same';
end

if streq(arg.type, 'imfilter,mirror') && exist('imfilter') ~= 2
	fail 'no imfilter (no image processing toolbox)'
end

if streq(arg.type, 'imfilter,same') && exist('imfilter') ~= 2
	warn 'no imfilter (no image processing toolbox) so using slower convn'
	arg.type = 'conv,same';
end

arg.psf = Gblur_mask_psf_odd(arg.psf);

psf_flip = conj(flipdims(arg.psf, 'odd', 1));
does_many = false;

arg.odim = size(mask);
if ndims(mask) == 2 && size(mask,2) == 1 % trick for 1D
	arg.odim = size(mask,1);
end
arg.idim = arg.odim;

switch arg.type

case 'conv,same'
	arg.psf_flip = psf_flip;
	handle_forw = @(arg,x) convn(x, arg.psf, 'same');
	handle_back = @(arg,y) fatrix2_maskit(arg.mask, ...
			convn(y, arg.psf_flip, 'same'));
	does_many = true;

case 'conv,per'
	arg.psf_flip = psf_flip;
	handle_forw = @(arg,x) ir_conv(x, arg.psf, 'per', true);
	handle_back = @(arg,y) fatrix2_maskit(arg.mask, ...
			ir_conv(y, arg.psf_flip, 'per', true));

case 'conv,valid'
	arg.psf_flip = psf_flip;
	handle_forw = @(arg,x) convn(x, arg.psf, 'valid');
	handle_back = @(arg,y) convn(y, arg.psf_flip);
	arg.odim = arg.idim - size(arg.psf) + 1;
	does_many = true;

case 'fft,same'
	[arg, handle_forw, handle_back] = Gblur_setup_fft_same(arg);

case 'imfilter,circ'
	arg.psf_flip = psf_flip;
	handle_forw = @(arg,x) imfilter(x, arg.psf, 'conv', 'same', ...
		'circular', arg.imfilter_options{:});
	handle_back = @(arg,y) fatrix2_maskit(arg.mask, ...
			imfilter(y, arg.psf_flip, 'conv', 'same', ...
				'circular', arg.imfilter_options{:}));

case 'imfilter,mirror'
	arg.psf_flip = psf_flip;
	handle_forw = @(arg,x) imfilter(x, arg.psf, 'conv', 'same', ...
		'symmetric', arg.imfilter_options{:});
	handle_back = @(arg,y) fatrix2_maskit(arg.mask, ...
			imfilter(y, arg.psf_flip, 'conv', 'same', ...
				'symmetric', arg.imfilter_options{:}));

case 'imfilter,same'
	arg.psf_flip = psf_flip;
	handle_forw = @(arg,x) imfilter(x, arg.psf, 'conv', 'same', ...
		arg.imfilter_options{:});
	handle_back = @(arg,y) fatrix2_maskit(arg.mask, ...
			imfilter(y, arg.psf_flip, 'conv', 'same', ...
				arg.imfilter_options{:}));

case 'sparse'
	ob = Gblur_sparse(arg.psf, arg.idim);
	ob = ob(:,arg.mask(:));
	return

otherwise
	error 'unknown blur type'
end

switch arg.class

case 'Fatrix'

	arg.nd = prod(arg.odim);
	arg.np = sum(mask(:));
	dim = [arg.nd arg.np]; % trick: make it masked by default!

	ob = Fatrix(dim, arg, 'caller', 'Gblur', ...
		'abs', @Gblur_abs, ...
		'forw', @Gblur_forw_Fatrix, 'back', @Gblur_back_Fatrix);

case 'fatrix2'

	ob = fatrix2('idim', arg.idim, 'odim', arg.odim, ...
		'arg', arg, 'does_many', does_many, ...
		'abs', @Gblur_abs, 'forw', handle_forw, 'back', handle_back);

otherwise
	fail('bug')
end

% Gblur_mask_psf_odd(psf)
% having an odd-sized psf facilites adjoint
function psf = Gblur_mask_psf_odd(psf)
nd = ndims(psf);
for id=1:nd
	sz = size(psf, id);
	if ~mod(sz, 2) % even
		warn('size(psf, %d) = %d is even; appending 0', id, sz)
		tmp = size(psf);
		tmp(id) = 1;
		z = zeros(tmp);
		psf = cat(id, psf, z);
	end
end

% Gblur_sparse()
% a_ij = b[ n(i) - n(j), m(i) - m(j) ]
% n(i) = (i-1) mod N
% m(i) = floor((i-1) / N)
function sp = Gblur_sparse(psf, idim)
if numel(idim) ~= 2
	fail 'not done'
end
fail 'todo: not done'
%sp = sparse(i, j, s, length(ii), size(ob.arg.G, 2));

% Gblur_abs(): |A| for abs(A)
function ob = Gblur_abs(ob)
ob.arg.psf = abs(ob.arg.psf);
if isfield(ob.arg, 'psf_flip')
	ob.arg.psf_flip = abs(ob.arg.psf_flip);
end
if isfield(ob.arg, 'psf_fft') % fixed 2012-04-08
	ob.arg.psf_fft = Gblur_setup_fft_same_fft(ob.arg.psf, ob.mask);
end

% Gblur_setup_fft_same()
function [arg, handle_forw, handle_back] = Gblur_setup_fft_same(arg)

arg.psf_fft = Gblur_setup_fft_same_fft(arg.psf, arg.mask);

handle_forw = @(arg,x) ifftn(fftn(x) .* arg.psf_fft);
handle_back = @(arg,y) fatrix2_maskit(arg.mask, ...
			ifftn(fftn(y) .* conj(arg.psf_fft)));

% Gblur_setup_fft_same_fft()
function psf_fft = Gblur_setup_fft_same_fft(psf, mask)

% put psf in center of array
tmp = zeros(size(mask));
switch ndims(psf)
case 2
	[n1 n2] = size(mask);
	[p1 p2] = size(psf);
%	h1 = (size(psf,1)-1)/2;
%	h2 = (size(psf,2)-1)/2;
%	i1 = floor(n1/2) + 1 + [-h1:h1]
%	i2 = floor(n2/2) + 1 + [-h2:h2]
	i1 = floor(n1/2) + 1 + (1:p1) - floor((p1+1)/2);
	i2 = floor(n2/2) + 1 + (1:p2) - floor((p2+1)/2);
	tmp(i1,i2) = psf;
case 3
	if any(~rem(size(psf),2)), error 'psf size must be odd', end
	h1 = (size(psf,1)-1)/2;
	h2 = (size(psf,2)-1)/2;
	h3 = (size(psf,3)-1)/2;
	i1 = floor(size(mask,1)/2) + 1 + [-h1:h1];
	i2 = floor(size(mask,2)/2) + 1 + [-h2:h2];
	i3 = floor(size(mask,3)/2) + 1 + [-h3:h3];
	tmp(i1,i2,i3) = psf;
otherwise
	error 'only 2d & 3d done'
end
psf_fft = fftn(ifftshift(tmp));

% Gblur_forw_Fatrix(): y = A * x
function y = Gblur_forw_Fatrix(arg, x)

[x ei] = embed_in(x, arg.mask, arg.np);

switch arg.type
case 'conv,same'
	y = convn(x, arg.psf, 'same');
case 'conv,per'
	y = ir_conv(x, arg.psf, 'per', true);
case 'fft,same'
	if ndims(x) > ndims(arg.mask);
		y = fft_conv_multi(x, arg.psf_fft);
	else
		y = ifftn(fftn(x) .* arg.psf_fft);
	end
case 'imfilter,circ'
	y = ir_imfilter_many(x, arg.psf, 'conv', 'same', ...
		'circular', arg.imfilter_options{:});
case 'imfilter,same'
	y = ir_imfilter_many(x, arg.psf, 'conv', 'same', ...
		arg.imfilter_options{:});
otherwise
	error 'bug'
end

y = ei.shape(y);

% Gblur_back_Fatrix(): x = A' * y
function x = Gblur_back_Fatrix(arg, y)

[y eo] = embed_out(y, arg.odim);

switch arg.type
case 'conv,same'
	x = convn(y, arg.psf_flip, 'same');
case 'conv,per'
	x = ir_conv(y, arg.psf_flip, 'per', true);
case 'fft,same'
	if ndims(y) > ndims(arg.mask);
		x = fft_conv_multi(y, conj(arg.psf_fft));
	else
		x = ifftn(fftn(y) .* conj(arg.psf_fft));
	end
case 'imfilter,circ'
	x = ir_imfilter_many(y, arg.psf_flip, 'conv', 'same', ...
		'circular', arg.imfilter_options{:});
case 'imfilter,same'
	x = ir_imfilter_many(y, arg.psf_flip, 'conv', 'same', ...
		arg.imfilter_options{:});
otherwise
	error 'bug'
end

x = eo.shape(x, arg.mask, arg.np);

% fft_conv_multi()
function y = fft_conv_multi(x, H)
dim = size(x);
y = zeros(size(x));
y = [];
for ii=1:dim(end)
	tmp = ifftn(fftn(stackpick(x, ii)) .* H);
	y = cat(length(dim), y, tmp); % slow: keeps enlarging y.
end


function obj = startfd(org)
    % This is the starting code for the fractal dimension, which manages the different codes.
    % Francesco Pacchiani 1/2000
    %
    %
    % disp('fractal/codes/startfd.m');
    %
    %
    % turned into function by Celso G Reyes 2017

    ZG=ZmapGlobal.Data; % used by get_zmap_globals
    switch (org)

        case 1 	% Call from TIMEPLOT, FDSPHERE, DORAND

            range = 1;
            radm = [];
            rasm = [];
            obj=fractal()
            obj.fdparain(obj.gobut);

        case 2	% Call from FDPARAIN

            dtokm = 1;
            pdc3;

        case 3	% Call from TIMEPLOT
            %
            % Default values
            %
            numran = 1000;				% # of points in random catalog
            distr = 1;
            stdx = 0.5;
            stdy = 0.5;
            stdz = 1;
            long1 = min(obj.catalog.Longitude);
            long2 = max(obj.catalog.Longitude);
            lati1 = min(obj.catalog.Latitude);
            lati2 = max(obj.catalog.Latitude);
            dept1 = min(abs(obj.catalog.Depth));
            dept2 = max(abs(obj.catalog.Depth));

            obj.E = obj.catalog;
            obj.randomcat();

        case 4	% Call from TIMEPLOT

            nev = 600;
            inc = 100;
            fdtimin;

        case 5  % Call from VIEW_DV

            range = 1;
            radm = [];
            rasm = [];
            crclparain;

    end

end


function t_om_solve_qps(quiet)
%T_OM_SOLVE_QPS  Tests of QP solvers via OM.SOLVE().

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

if nargin < 1
    quiet = 0;
end

algs = {'DEFAULT', 'BPMPD', 'MIPS', 250, 'IPOPT', 'OT', 'CPLEX', 'MOSEK', 'GUROBI', 'CLP', 'GLPK', 'OSQP'};
names = {'DEFAULT', 'BPMPD_MEX', 'MIPS', 'sc-MIPS', 'IPOPT', 'linprog/quadprog', 'CPLEX', 'MOSEK', 'Gurobi', 'CLP', 'glpk', 'OSQP'};
check = {[], 'bpmpd', [], [], 'ipopt', 'quadprog', 'cplex', 'mosek', 'gurobi', 'clp', 'glpk', 'osqp'};
does_qp = [1 1 1 1 1 1 1 1 1 1 0 1];

n = 30;
nqp = 21;
t_begin(27+n*length(algs), quiet);

diff_alg_warn_id = 'optim:linprog:WillRunDiffAlg';
if have_feature('quadprog') && have_feature('quadprog', 'vnum') == 7.005
    s1 = warning('query', diff_alg_warn_id);
    warning('off', diff_alg_warn_id);
end

for k = 1:length(algs)
    if ~isempty(check{k}) && ~have_feature(check{k})
        t_skip(n, sprintf('%s not installed', names{k}));
    else
        opt = struct('verbose', 0, 'alg', algs{k});
        mpopt = struct( ...
            'verbose', 0, ...
            'opf', struct( ...
                'violation', 5e-6 ), ...
            'cplex', struct( ...
                'lpmethod', 0, ...
                'qpmethod', 0, ...
                'opt', 0 ), ...
            'mosek', struct( ...
                'lp_alg', 0, ...
                'max_it', 0, ...
                'gap_tol', 0, ...
                'max_time', 0, ...
                'num_threads', 0, ...
                'opt', 0 ) ...
        );
        switch names{k}
            case {'DEFAULT', 'MIPS', 'sc-MIPS'}
                opt.mips_opt.comptol = 1e-8;
%             case 'linprog/quadprog'
%                 opt.verbose = 2;
%                 opt.linprog_opt.Algorithm = 'interior-point';
%                 opt.linprog_opt.Algorithm = 'active-set';
%                 opt.linprog_opt.Algorithm = 'simplex';
%                 opt.linprog_opt.Algorithm = 'dual-simplex';
%             end
            case 'CPLEX'
%               alg = 0;        %% default uses barrier method with NaN bug in lower lim multipliers
                alg = 2;        %% use dual simplex
                mpopt.cplex.lpmethod = alg;
                mpopt.cplex.qpmethod = min([4 alg]);
                opt.cplex_opt = cplex_options([], mpopt);
            case 'MOSEK'
%                 sc = mosek_symbcon;
%                 alg = sc.MSK_OPTIMIZER_DUAL_SIMPLEX;    %% use dual simplex
%                 alg = sc.MSK_OPTIMIZER_INTPNT;          %% use interior point
%                 mpopt.mosek.lp_alg = alg;
                mpopt.mosek.gap_tol = 1e-10;
%                 mpopt.mosek.opts.MSK_DPAR_INTPNT_TOL_PFEAS = 1e-10;
%                 mpopt.mosek.opts.MSK_DPAR_INTPNT_TOL_DFEAS = 1e-10;
%                 mpopt.mosek.opts.MSK_DPAR_INTPNT_TOL_INFEAS = 1e-10;
%                 mpopt.mosek.opts.MSK_DPAR_INTPNT_TOL_REL_GAP = 1e-10;
                vnum = have_feature('mosek', 'vnum');
                if vnum >= 8
%                     mpopt.mosek.opts.MSK_DPAR_INTPNT_QO_TOL_PFEAS = 1e-10;
%                     mpopt.mosek.opts.MSK_DPAR_INTPNT_QO_TOL_DFEAS = 1e-10;
%                     mpopt.mosek.opts.MSK_DPAR_INTPNT_QO_TOL_INFEAS = 1e-10;
%                     mpopt.mosek.opts.MSK_DPAR_INTPNT_QO_TOL_MU_RED = 1e-10;
                    mpopt.mosek.opts.MSK_DPAR_INTPNT_QO_TOL_REL_GAP = 1e-10;
                end
%                 opt.verbose = 3;
                opt.mosek_opt = mosek_options([], mpopt);
            case 'OSQP'
                opt.osqp_opt.polish = 1;
%                 opt.osqp_opt.alpha = 1;
%                 opt.osqp_opt.eps_abs = 1e-8;
%                 opt.osqp_opt.eps_rel = 1e-10;
%                 opt.osqp_opt.eps_prim_inf = 1e-8;
%                 opt.osqp_opt.eps_dual_inf = 1e-8;
        end

        t = sprintf('%s - 3-d LP : ', names{k});
        %% based on example from 'doc linprog'
        c = [-5; -4; -6];
        A = [1 -1  1;
             -3  -2  -4;
             3  2  0];
        l = [-Inf; -42; -Inf];
        u = [20; Inf; 30];
        xmin = [0; 0; 0];
        x0 = [];
        om = opt_model;
        om.add_var('x', 3, x0, xmin);
        om.add_quad_cost('c', [], c);
        om.add_lin_constraint('Ax', A, l, u);
        [x, f, s, out, lam] = om.solve(opt);
        t_is(s, 1, 12, [t 'success']);
        t_is(x, [0; 15; 3], 6, [t 'x']);
        t_is(f, -78, 6, [t 'f']);
        t_is(lam.mu_l, [0;1.5;0], 9, [t 'lam.mu_l']);
        t_is(lam.mu_u, [0;0;0.5], 9, [t 'lam.mu_u']);
        if strcmp(algs{k}, 'CLP') && ~have_feature('opti_clp')
            t_skip(2, [t 'lam.lower/upper : MEXCLP does not return multipliers on var bounds']);
        else
            t_is(lam.lower, [1;0;0], 9, [t 'lam.lower']);
            t_is(lam.upper, zeros(size(x)), 9, [t 'lam.upper']);
        end
        t_ok(~isfield(om.soln, 'var'), [t 'no parse_soln() outputs']);

        if does_qp(k)
            t = sprintf('%s - unconstrained 3-d quadratic : ', names{k});
            %% from http://www.akiti.ca/QuadProgEx0Constr.html
            H = [5 -2 -1; -2 4 3; -1 3 5];
            c = [2; -35; -47];
            x0 = [0; 0; 0];
            om = opt_model;
            om.add_var('x', 3, x0);
            om.add_quad_cost('c', H, c);
            [x, f, s, out, lam] = om.solve(opt);
            t_is(s, 1, 12, [t 'success']);
            t_is(x, [3; 5; 7], 7, [t 'x']);
            t_is(f, -249, 11, [t 'f']);
            t_ok(isempty(lam.mu_l), [t 'lam.mu_l']);
            t_ok(isempty(lam.mu_u), [t 'lam.mu_u']);
            t_is(lam.lower, zeros(size(x)), 13, [t 'lam.lower']);
            t_is(lam.upper, zeros(size(x)), 13, [t 'lam.upper']);

            t = sprintf('%s - constrained 2-d QP : ', names{k});
            %% example from 'doc quadprog'
            H = [   1   -1;
                    -1  2   ];
            c = [-2; -6];
            A = [   1   1;
                    -1  2;
                    2   1   ];
            l = [];
            u = [2; 2; 3];
            xmin = [0; 0];
            x0 = [];
            om = opt_model;
            om.add_var('x', 2, x0, xmin);
            om.add_quad_cost('c', H, c);
            om.add_lin_constraint('Ax', A, l, u);
            [x, f, s, out, lam] = om.solve(opt);
            t_is(s, 1, 12, [t 'success']);
            t_is(x, [2; 4]/3, 7, [t 'x']);
            t_is(f, -74/9, 6, [t 'f']);
            t_is(lam.mu_l, [0;0;0], 13, [t 'lam.mu_l']);
            t_is(lam.mu_u, [28;4;0]/9, 4, [t 'lam.mu_u']);
            if strcmp(algs{k}, 'CLP') && ~have_feature('opti_clp')
                t_skip(2, [t 'lam.lower/upper : MEXCLP does not return multipliers on var bounds']);
            else
                t_is(lam.lower, zeros(size(x)), 7, [t 'lam.lower']);
                t_is(lam.upper, zeros(size(x)), 13, [t 'lam.upper']);
            end

            t = sprintf('%s - constrained 4-d QP : ', names{k});
            %% from https://v8doc.sas.com/sashtml/iml/chap8/sect12.htm
            H = [   1003.1  4.3     6.3     5.9;
                    4.3     2.2     2.1     3.9;
                    6.3     2.1     3.5     4.8;
                    5.9     3.9     4.8     10  ];
            c = zeros(4,1);
            A = [   1       1       1       1;
                    0.17    0.11    0.10    0.18    ];
            l = [1; 0.10];
            u = [1; Inf];
            xmin = zeros(4,1);
            x0 = [1; 0; 0; 1];
            om = opt_model;
            om.add_var('x', 4, x0, xmin);
            om.add_quad_cost('c', H, c);
            om.add_lin_constraint('Ax', A, l, u);
            [x, f, s, out, lam] = om.solve(opt);
            t_is(s, 1, 12, [t 'success']);
            t_is(x, [0; 2.8; 0.2; 0]/3, 5, [t 'x']);
            t_is(f, 3.29/3, 6, [t 'f']);
            t_is(lam.mu_l, [6.58;0]/3, 6, [t 'lam.mu_l']);
            t_is(lam.mu_u, [0;0], 13, [t 'lam.mu_u']);
            if strcmp(algs{k}, 'CLP') && ~have_feature('opti_clp')
                t_skip(2, [t 'lam.lower/upper : MEXCLP does not return multipliers on var bounds']);
            else
                t_is(lam.lower, [2.24;0;0;1.7667], 4, [t 'lam.lower']);
                t_is(lam.upper, zeros(size(x)), 13, [t 'lam.upper']);
            end
        else
            t_skip(nqp, sprintf('%s does not handle QP problems', names{k}));
        end

        t = sprintf('%s - infeasible LP : ', names{k});
        p = struct('A', sparse([1 1]), 'c', [1;1], 'u', -1, 'xmin', [0;0], 'opt', opt);
        [x, f, s, out, lam] = qps_master(p);
        t_ok(s <= 0, [t 'no success']);
    end
end

t = 'om.soln.';
opt.alg = 'DEFAULT';
%% from https://v8doc.sas.com/sashtml/iml/chap8/sect12.htm
H = [   1003.1  4.3     6.3     5.9;
        4.3     2.2     2.1     3.9;
        6.3     2.1     3.5     4.8;
        5.9     3.9     4.8     10  ];
c = zeros(4,1);
A = [   1       1       1       1;
        0.17    0.11    0.10    0.18    ];
l = [1; 0.10];
u = [1; Inf];
xmin = zeros(4,1);
x0 = [1; 0; 0; 1];
om = opt_model;
om.add_var('x', 4, x0, xmin);
om.add_quad_cost('c', H, c);
om.add_lin_constraint('Ax', A, l, u);
opt.parse_soln = 1;
[x, f, s, out, lam] = om.solve(opt);
t_is(om.soln.x, x, 14, [t 'x']);
t_is(om.soln.f, f, 14, [t 'f']);
t_is(om.soln.eflag, s, 14, [t 'eflag']);
t_str_match(om.soln.output.alg, out.alg, [t 'output.alg']);
t_is(om.soln.lambda.lower, lam.lower, 14, [t 'om.soln.lambda.lower']);
t_is(om.soln.lambda.upper, lam.upper, 14, [t 'om.soln.lambda.upper']);
t_is(om.soln.lambda.mu_l, lam.mu_l, 14, [t 'om.soln.lambda.mu_l']);
t_is(om.soln.lambda.mu_u, lam.mu_u, 14, [t 'om.soln.lambda.mu_u']);

t = 'om.get_soln(''var'', ''x'') : ';
[x1, mu_l, mu_u] = om.get_soln('var', 'x');
t_is(x1, x, 14, [t 'x']);
t_is(mu_l, lam.lower, 14, [t 'mu_l']);
t_is(mu_u, lam.upper, 14, [t 'mu_u']);

t = 'om.get_soln(''var'', ''mu_l'', ''x'') : ';
t_is(om.get_soln('var', 'mu_l', 'x'), lam.lower, 14, [t 'mu_l']);

t = 'om.get_soln(''lin'', ''Ax'') : ';
[g, mu_l] = om.get_soln('lin', 'Ax');
t_is(g{1}, A*x-u, 14, [t 'A * x - u']);
t_is(g{2}, l-A*x, 14, [t 'l - A * x']);
t_is(mu_l, lam.mu_l, 14, [t 'mu_l']);

t = 'om.get_soln(''lin'', {''mu_u'', ''mu_l'', ''Ax_u''}, ''Ax'') : ';
[mu_u, mu_l, g] = om.get_soln('lin', {'mu_u', 'mu_l', 'Ax_u'}, 'Ax');
t_is(g, A*x-u, 14, [t 'A * x - u']);
t_is(mu_l, lam.mu_l, 14, [t 'mu_l']);
t_is(mu_u, lam.mu_u, 14, [t 'mu_u']);

t = 'om.get_soln(''qdc'', ''c'') : ';
[f1, df, d2f] = om.get_soln('qdc', 'c');
t_is(f1, f, 14, [t 'f']);
t_is(df, H*x+c, 14, [t 'df']);
t_is(d2f, H, 14, [t 'd2f']);

t = 'om.get_soln(''qdc'', ''df'', ''c'') : ';
df = om.get_soln('qdc', 'df', 'c');
t_is(df, H*x+c, 14, [t 'df']);

t = 'parse_soln : ';
t_is(om.soln.var.val.x, om.get_soln('var', 'x'), 14, [t 'var.val.x']);
t_is(om.soln.var.mu_l.x, om.get_soln('var', 'mu_l', 'x'), 14, [t 'var.mu_l.x']);
t_is(om.soln.var.mu_u.x, om.get_soln('var', 'mu_u', 'x'), 14, [t 'var.mu_u.x']);
t_is(om.soln.lin.mu_l.Ax, om.get_soln('lin', 'mu_l', 'Ax'), 14, [t 'lin.mu_l.Ax']);
t_is(om.soln.lin.mu_u.Ax, om.get_soln('lin', 'mu_u', 'Ax'), 14, [t 'lin.mu_u.Ax']);

if have_feature('quadprog') && have_feature('quadprog', 'vnum') == 7.005
    warning(s1.state, diff_alg_warn_id);
end

t_end;


function ret = my_gpu_conv2(l_prev_layer, filter)
    ret = conv2(l_prev_layer,filter,'valid');
end


function [tSnrImage, fileTsnr, tSnrRatioImage, fileTsnrRatio] = ...
    tapas_physio_compute_tsnr_spm(SPM, iC, iCForRatio, doInvert, doSaveNewContrasts)
% Computes temporal SNR image after correcting for a contrast
% from SPM general linear model
%
%   tSnrImage = compute_tsnr_spm(input)
%
%   This function executes the following steps:
%   1.  Compute F-Contrasts of the kind "All-but-selected regressors"
%      	run spm_write_residuals to retrieve Y-Yc = Y0 + e
%       (note that roles of Y0 and Yc are swapped compared to in spm_spm,
%       since we are interested in Residuals that do *not* stem from our
%       regressors, but the other ones!
%   2.  Compute tSNR images using mean(Residuals)/std(Residuals) from
%       F-contrasts from (1.), save as nii-files tsnr_con<iC>.nii
%   3.  Compute tSNR for contrast of comparison, default:
%       preprocessed time-series, after pre-whitening and
%       highpass-filtering (i.e. K*W*Y), saved as nii-files
%       using a recursive call to this function
%   4.  Compute tSNR ratio images from (3.) and (2.), save as nii-files
%       tsnr_con<iC>vs<iCRatio>.nii
%
% IN
%   SPM     SPM variable (in SPM.mat, or file name) after parameter and
%           contrast estimation
%   iC      Contrast of interest for which tSNR is computed
%           iC = 0 computes tSNR of the raw image time series after
%               pre-whitening and filtering, but without any model
%               confound regressors removed. (default)
%           iC = NaN computes tSNR after removing the full model (including
%               the mean), and is therefore equivalent to sqrt(1/ResMS)
%   iCForRatio
%           For computation of relative tSNR.
%           Contrast index whose tSNR image should be used as a
%           denominator for tSnrRatioImage = tSNRiC./tSNRiCForRatio;
%           tSNR is compared to the tSNR image of the specified contrast
%           (0 for raw tSNR). The corresponding tSNR-image will be created
%           default = 0 (raw tSNR); Leave [] to not compute ratio of tSNRs
%   doInvert true (default for iC > 0 and ~nan) or false
%           typically, one is interested in the tSNR *after* correcting for
%           the contrast in question. To compute this, one has to look at
%           the residuals of the inverse F-contrast that excludes all but
%           the contrast of interest, i.e. eye(nRegressors) - xcon
%           Thus, this function computes this contrast per default and goes
%           from there determining residuals etc.
%   doSaveNewContrasts
%           true or false (default)
%           if true, the temporary contrasts created by inversion of
%           selected columns will be saved into the SPM structure.
%           Otherwise, all temporary data will be removed.
%
% OUT
%   tSnrImage   [nX,nY,nZ] image matrix holding tSNR when only including regressors in
%               contrast iC in design matrix;
%               i.e. gives the effect of
%                       mean(Xc*bc + e)/std(Xc*bc + e)
%               if Xc hold only regressors of iC
%
%               NOTE: If you want to estimate the effect of a noise
%               correction using some regressors, the Contrast with index
%               iC should actually contain an F-contrast *EXCLUDING* these
%               regressors, because everything else constitutes the
%               regressors of interest
%   fileTsnr    tsnr_con<iC>.nii
%               path and file name of tSNR image, if doSave was true
%   tSnrRatioImage
%               tSNR_con<iC>./tSNR_con<iCForRatio>
%               (if iCForRatio was specified)
%   fileTsnrRatio
%               tsnr_con<iC>vs<iCRatio>.nii
%               file where tSnrRatio was saved
%
% EXAMPLE
%   compute_tsnr_spm
%
%   See also spm_write_residuals spm_FcUtil

% Author: Lars Kasper
% Created: 2014-12-10
% Copyright (C) 2014 Institute for Biomedical Engineering, ETH/Uni Zurich.

if nargin < 2
    iC = 0;
end

if nargin < 3
    iCForRatio = 0;
end

if nargin < 4
    doInvert = true;
end

if nargin < 5
    doSaveNewContrasts = false;
end

doComputeRatio = ~isempty(iCForRatio);

% load SPM-variable, if filename given
if ~isstruct(SPM)
    % load SPM variable from file
    if iscell(SPM)
        fileSpm = SPM{1};
    else
        fileSpm = SPM;
    end
    load(fileSpm, 'SPM');
end

oldDirSpm = SPM.swd;

if ~doSaveNewContrasts
    % temporary changes to SPM structure saved in sub-dir, removed later
    newDirSpm = fullfile(SPM.swd, 'tmp');
    mkdir(newDirSpm);
    copyfile(fullfile(SPM.swd, '*.nii'), newDirSpm);
    copyfile(fullfile(SPM.swd, 'SPM.mat'), newDirSpm);
    SPM.swd = newDirSpm;
end

iCIn = iC;

isInvertableContrast = iC > 0 && ~isnan(iC);

if isInvertableContrast && doInvert
    if ~isequal(SPM.xCon(iC).STAT, 'F')
        error('Can only invert F-contrasts');
    end

    idxColumnsContrast = find(sum(SPM.xCon(iC).c));

    Fc = spm_FcUtil('Set', ['All but: ' SPM.xCon(iC).name], 'F', ...
        'iX0', idxColumnsContrast, SPM.xX.xKXs);
    SPM.xCon(end+1) = Fc;
    SPM = spm_contrasts(SPM,numel(SPM.xCon));

    % use this for computation
    iC = numel(SPM.xCon);
end

%% Write residuals Y - Y0 = Yc + e;
VRes        = spm_write_residuals(SPM, iC);
nVolumes    = numel(VRes);
for iVol = 1:nVolumes
    VRes(iVol).fname = fullfile(SPM.swd, VRes(iVol).fname);
end

fileTsnr = fullfile(oldDirSpm, sprintf('tSNR_con%04d.nii', iCIn));

useMrImage = false;

if useMrImage % use toolbox functionality
    ResImage    = MrImage(VRes(1).fname);

    % Create 4D image of contrast-specific "residuals", i.e. Xc*bc + e
    for iVol = 1:nVolumes
        ResImage.append(VRes(iVol).fname);
    end

    % compute tSNR = mean(Xc*bc + e)/std(Xc*bc + e)
    tSnrImage = ResImage.mean./ResImage.std;
    if doSaveNewContrasts
        tSnrImage.save(fileTsnr);
    end
else
    ResImage = spm_read_vols(VRes);
    meanImage = mean(ResImage, 4);
    stdImage = std(ResImage, 0, 4);
    tSnrImage = meanImage./stdImage;
    VTsnr = VRes(1);
    VTsnr.fname = fileTsnr;
    spm_write_vol(VTsnr, tSnrImage);
end

%%
if doComputeRatio
    fileTsnrCompare = fullfile(oldDirSpm, sprintf('tSNR_con%04d.nii', iCForRatio));

    % Load or compute tSNR image for comparison contrast
    if ~exist(fileTsnrCompare, 'file');
        % when computed, don't compute another ratio, and don't delete tmp
        % here! (will be done at the end)
        tSnrCompareImage = tapas_physio_compute_tsnr_spm(...
            fullfile(oldDirSpm, 'SPM.mat'), ...
            iCForRatio, doInvert, [], 1);
    else
        VCompare = spm_vol(fileTsnrCompare);
        tSnrCompareImage = spm_read_vols(VCompare);
    end

    % compute tSNR ratio and save
    tSnrRatioImage = tSnrImage./tSnrCompareImage;

    fileTsnrRatio = fullfile(oldDirSpm, ...
        sprintf('tSNRRatio_con%04dvs%04d.nii', iCIn, iCForRatio));
    VRatio = spm_vol(fileTsnrCompare);
    VRatio.fname = fileTsnrRatio;
    spm_write_vol(VRatio, tSnrRatioImage);
else
    tSnrRatioImage = [];
    fileTsnrRatio = [];
end

%% clean up all created residual files and temporary SPM folder
if ~doSaveNewContrasts
    delete(fullfile(newDirSpm, '*'));
    rmdir(newDirSpm);
else
    % delete at least the Res-images
    delete(fullfile(oldDirSpm, 'Res_*.nii'));
end


filename = 'CantileverSquare';%'CantileverSquareSYmmetricMesh';%'CantileverSquareNewFine';%'LshapeTriFine';%'CantileverSquareNew';'ArchTriFine';%'CantileverSquare';%'ArchTriFine';%'CantileverSquare';%'Lshape';%'LshapeTriFine';%'CantileverSquareSmall';%'';%'Lshape';'CantileverSquare';'Lshape';%'CantileverSquare';%'LshapeFine';%'Bridge_quad_coarse';%'Arch_quad_coarse';%'BridgeCool_Quadrilateral_Bilinear_Structured_Coarse';%'Bridge';%'CantileverSquareSmall';
ptype = 'MACRO';
initial_case = 'given';
m1 = 0.0101;
m2 = 0.0101;
%cost = {'compliance'};
cost = {'stressNorm'};
%cost = {'stressNorm','compliance'};
%weights = [0.55,0.45];
weights = 1;
constraint = {'volumeConstraint'};
filterType = 'P1';
constraint_case = 'EQUALITY';

Vfrac_initial = 0.3;
optimality_initial = 1e-5;
constr_initial = 1e-5;

Vfrac_final = 0.3;
optimality_final = 1e-5;
constr_final = 1e-5;

stressNormExponent_initial = 2;
stressNormExponent_final = 64;

optimizer = 'DualNestedInPrimal';
optimizerUnconstrained = 'PROJECTED GRADIENT';

designVariable = 'MicroParams';
ub = 0.989;
lb = 0.011;
homegenizedVariablesComputer = 'ByVademecum';
%
vademecumFileName = 'SuperEllipseQMax';
%vademecumFileName = 'SuperEllipseQ2';
%vademecumFileName = 'SuperEllipseQOptAnalytic';
%
% designVariable = 'Density';
% homegenizedVariablesComputer = 'ByInterpolation';
% method = 'SIMPALL';
% materialType = 'ISOTROPIC';

kfrac = 2;
nsteps = 20;

plotting = false;
printing = true;
monitoring = true;
monitoring_interval = 1;
maxiter = 500;


function organizeFusedTextures_HN(pathWORK,nPatient,CTinv_cell,CTweight_mat,R_mat,scale_cell,algo_cell,Ng_mat)
% -------------------------------------------------------------------------
% function organizeFusedTextures_HN(pathWORK,nPatient,CTinv_cell,CTweight_mat,R_mat,scale_cell,algo_cell,Ng_mat)
% -------------------------------------------------------------------------
% DESCRIPTION:
% This function computes FUSED texture features for all patients, for all
% different combinations of the following texture extraction parameters:
% - CT Inv.: Inversion of CT intensities in the PET/CT fusion process.
% - CT weight: Weight given to MRI wavelet low-pass sub-bands in the
%               PET/CT fusion process.
% - WPBF ratio 'R': Ratio specifying the ratio of weight given to band-pass
%                   coefficients to the weigth given to the rest of
%                   coefficients (HHH and LLL) in the wavelet band-pass
%                   filtering process.
% - Scale: Resolution at which the ROI is isotropically resampled.
% - Quant. Algo.: Quantization algorithm used on analyzed ROI.
% - Ng: Number of gray-levels in the quantization process.
%
% Different extraction parameters are passed as arrays or cells in the
% function in order to test all possible combinations. This function is
% used for FUSED scans specifically. See Ref. [1,2] and 'prepareVolume.m'
% for more details.
%
% Textures are organized in one cell: 'textures_PET_CT', where each cell
% entry organizes the data for all patients for a specific extraction
% parameter combination.
%
% -- > For example, size(textures_PET_CT) is equal to:
% [numel(CTinv_cell),numel(CTweight_mat),numel(R_mat),numel(scale_cell),numel(algo_cell),numel(Ng_mat)]
%
% In addition, the Spearman's rank correlation between each texture
% features and lung metastases is computed and saved in the cells for each
% texture feature of each entry. Results are saved in the STS WORKSPACE
% with the names given above.
% -------------------------------------------------------------------------
% REFERENCES:
% [1] Vallieres, M. et al. (2015). FDG-PET/CT radiomics models for the
%     early prediction of different tumour outcomes in head and neck cancer.
%     The Journal of Nuclear Medicine, aa(bb), xxx-yyy.
%     doi:
% [2] Vallieres, M. et al. (2015). A radiomics model from joint FDG-PET and
%     MRI texture features for the prediction of lung metastases in soft-tissue
%     sarcomas of the extremities. Physics in Medicine and Biology, 60(14),
%     5471-5496. doi:10.1088/0031-9155/60/14/5471
% -------------------------------------------------------------------------
% INPUTS:
% - pathWORK: Full path to the HN WORKSPACE directory.
% - nPatient: Number of patients to read.
% - CTinv_cell: Cell vector specifying the different 'CT Inv.' values to test.
% - CTweight_mat: Array vector specifying the different 'CT weight' values to test.
% - R_mat: Array vector specifying the different 'R' ratio values to test.
% - scale_cell: Cell vector specifying the different 'Scale' values to test.
% - algo_cell: Cell vector specifying the different 'Quant. Algo.' values to test.
% - Ng_mat: Array vector specifying the different 'Ng' values to test.
% -------------------------------------------------------------------------
% EXAMPLE:
% CTinv_cell = {'NoInv','Inv'};
% CTweight_mat = [1/4,1/3,1/2,2/3,3/4];
% R_mat = [1/2,2/3,1,3/2,2];
% scale_cell = {'pixelW',1,2,3,4,5};
% algo_cell = {'Uniform','Equal','Lloyd'};
% Ng_mat = [8,16,32,64];
% -------------------------------------------------------------------------
% AUTHOR(S): Martin Vallieres <mart.vallieres@gmail.com>
% -------------------------------------------------------------------------
% HISTORY:
% - Creation: July 2015
%--------------------------------------------------------------------------
% STATEMENT:
% This file is part of <https://github.com/mvallieres/radiomics/>,
% a package providing MATLAB programming tools for radiomics analysis.
% --> Copyright (C) 2015  Martin Vallieres
%
%    This package 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 package 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 package.  If not, see <http://www.gnu.org/licenses/>.
% -------------------------------------------------------------------------

startpath = pwd;
cd(pathWORK), load('outcomes')

% INITIALIZATION
scans = {'PET_CT'};
nScans = numel(scans);
suffix = ['_TEXT'];
nameOutcomes = fieldnames(outcomes);
nOutcomes = length(nameOutcomes);

for scan = 1:numel(scans)
    cd([pathWORK,'/TEXTURES'])
    temp = load('Patient1_PET_CT_TEXT'); temp = struct2cell(temp); temp = temp{1}; % In order to get the necessary 'nameType' and 'nameFeature' fields
    nameType = fieldnames(temp.Experiment1); nType = numel(nameType); % All texture types are the same
    text = cell(numel(CTinv_cell),numel(CTweight_mat),numel(R_mat),numel(scale_cell),numel(algo_cell),numel(Ng_mat)); % Initialization of master cell
    tempText = cell(1,nPatient); % Cell used to load patient textures only once
    for p = 1:nPatient
        load(['Patient',num2str(p),'_',scans{scan},suffix]) % Variable 'textures' is now in MATLAB workspace
        tempText{p} = textures;
    end
    experiment = 0;
    for i = 1:numel(CTinv_cell)
        for w = 1:numel(CTweight_mat)
            for r = 1:numel(R_mat)
                for s = 1:numel(scale_cell)
                    for a = 1:numel(algo_cell)
                        for n = 1:numel(Ng_mat)
                            text{i,w,r,s,a,n} = struct;
                            experiment = experiment + 1;
                            strExperiment = ['Experiment',num2str(experiment)];
                            for t = 1:nType-1 % Last field is 'parameters'
                                nameFeature = fieldnames(temp.(strExperiment).(nameType{t})); nFeature = numel(nameFeature);
                                for f = 1:nFeature
                                    data = zeros(nPatient,1);
                                    for p = 1:nPatient
                                        data(p,1) = tempText{p}.(strExperiment).(nameType{t}).(nameFeature{f});
                                    end
                                    text{i,w,r,s,a,n}.(nameType{t}).(nameFeature{f}).Data = data;
                                    for o = 1:nOutcomes
                                        text{i,w,r,s,a,n}.(nameType{t}).(nameFeature{f}).Spearman.(nameOutcomes{o}) = corr(data,outcomes.(nameOutcomes{o}),'type','Spearman');
                                    end
                                end
                            end
                        end
                    end
                end
            end
        end
    end
    cd(pathWORK), save(['textures_',scans{scan}],'text')
end

cd(startpath)
end


function [] = outSVMData(f)
    fid = fopen('f.txt', 'w');
	for ii = 1 : size (f, 1)
	    fprintf (fid, '%f ', -1);
	    for jj = 1 : size (f, 2)
		    fprintf (fid, '%d:', jj);
		    fprintf (fid, '%f ', f(ii, jj));
		end
		fprintf (fid, '\n');
	end
	fclose (fid);
return;


function hm=plotsurf(node,face,varargin)
%
% hm=plotsurf(node,face,opt)
%
% plot 3D surface meshes
%
% author: Qianqian Fang <fangq at nmr.mgh.harvard.edu>
%
% input:
%      node: node coordinates, dimension (nn,3); if node has a
%            4th column, it will be used to set the color at each node.
%      face: triangular surface face list; if face has a 4th column,
%            it will be used to separate the surface into
%            sub-surfaces and display them in different colors;
%            face can be a cell array, each element of the array represents
%            a polyhedral facet of the mesh, if an element is an array with
%            two array subelements, the first one is the node index, the
%            second one is a scalar as the group id of the facet.
%      opt:  additional options for the plotting, see plotmesh
%
% output:
%   hm: handle or handles (vector) to the plotted surfaces
%
% example:
%
%   h=plotsurf(node,face);
%   h=plotsurf(node,face,'facecolor','r');
%
% -- this function is part of iso2mesh toolbox (http://iso2mesh.sf.net)
%

rngstate = rand ('state');

if(nargin>=2)
  randseed=hex2dec('623F9A9E'); % "U+623F U+9A9E"
  if(isoctavemesh) randseed=randseed+3; end
  if(~isempty(getvarfrom({'caller','base'},'ISO2MESH_RANDSEED')))
        randseed=getvarfrom({'caller','base'},'ISO2MESH_RANDSEED');
  end
  rand('state',randseed);

  if(iscell(face))
    sc=sparse(10,3); % face colormap
    sc(1:10,:)=rand(3,10)';
    len=length(face);
    newsurf=cell(1);
    % reorganizing each labeled surface into a new cell
    for i=1:len
        fc=face{i};
        if(iscell(fc) && length(fc)>=2)
            if(fc{2}+1>10)
                sc(fc{2}+1,:)=rand(1,3);
            end
            if(fc{2}+1>length(newsurf))
                newsurf{fc{2}+1}={};
            end
            newsurf{fc{2}+1}{end+1}=fc{1};
        else % unlabeled facet is tagged by 0
            if(iscell(fc))
                newsurf{1}{end+1}=cell2mat(fc);
            else
                newsurf{1}{end+1}=fc;
            end
        end
    end
    hold on;
	h=[];
    newlen=length(newsurf);

    for i=1:newlen
        if(isempty(newsurf{i})); continue; end
        try
            subface=cell2mat(newsurf{i}')';
            if(size(subface,1)>1 && ndims(subface)==2)
               subface=subface';
            end
            h=[h patch('Vertices',node,'Faces',subface,'facecolor',sc(i,:),varargin{:})];
        catch
            for j=1:length(newsurf{i})
                h=[h patch('Vertices',node,'Faces',newsurf{i}{j},'facecolor',sc(i,:),varargin{:})];
            end
        end
    end
  else
    if(size(face,2)==4)
        tag=face(:,4);
		types=unique(tag);
        hold on;
		h=[];
	    for i=1:length(types)
            if(size(node,2)==3)
                h=[h plotasurf(node,face(find(tag==types(i)),1:3),'facecolor',rand(3,1),varargin{:})];
            else
                h=[h plotasurf(node,face(find(tag==types(i)),1:3),varargin{:})];
            end
        end
    else
        h=plotasurf(node,face,varargin{:});
    end
  end
end
if(~isempty(h))
  axis equal;
  if(all(get(gca,'view')==[0 90]))
      view(3);
  end
end
if(~isempty(h) && nargout>=1)
  hm=h;
end

rand ('state',rngstate);

%-------------------------------------------------------------------------
function hh=plotasurf(node,face,varargin)
isoct=isoctavemesh;
if(size(node,2)==4)
	if(isoct && ~exist('trisurf','file'))
	    h=trimesh(face(:,1:3),node(:,1),node(:,2),node(:,3),node(:,4),'edgecolor','k',varargin{:});
	else
	    h=trisurf(face(:,1:3),node(:,1),node(:,2),node(:,3),node(:,4),varargin{:});
	end
else
	if(isoct && ~exist('trisurf','file'))
	    h=trimesh(face(:,1:3),node(:,1),node(:,2),node(:,3),'edgecolor','k',varargin{:});
	else
	    h=trisurf(face(:,1:3),node(:,1),node(:,2),node(:,3),varargin{:});
	end
end
if(exist('h','var')) hh=h; end


function bench_ssc_batch_omp(md)

num_samples_per_digit_arr = [50 80 100 150 200 300 400];

nns = numel(num_samples_per_digit_arr);
solver_name = 'ssc_batch_omp';
for ns=1:nns
    num_samples_per_digit = num_samples_per_digit_arr(ns);
    fprintf('Simulating: %s, for %d samples per digit\n', solver_name, num_samples_per_digit);
    result = check_digits(md, num_samples_per_digit, @ssc_batch_omp, solver_name);
    filepath = sprintf('bin/%s_digits=%d.mat', solver_name, num_samples_per_digit);
    save(filepath, 'result');
end

end


function [LB,UB,PLB,PUB,fixidx] = boundscheck(x0,LB,UB,PLB,PUB)
%BOUNDSCHECK Initial check of bounds and see if there are fixed variables.

nvars = numel(x0);

% Expand scalar inputs to full vectors
if isscalar(LB); LB = LB*ones(1,nvars); end
if isscalar(UB); UB = UB*ones(1,nvars); end
if isscalar(PLB); PLB = PLB*ones(1,nvars); end
if isscalar(PUB); PUB = PUB*ones(1,nvars); end

if isempty(PLB) || isempty(PUB)
    warning('bads:pbUnspecified', 'Plausible lower/upper bounds PLB and/or PUB not specified. Using hard upper/lower bounds instead.');
    if isempty(PLB); PLB = LB; end
    if isempty(PUB); PUB = UB; end
end

% Test that all vectors have the same length
if any([numel(LB),numel(UB),numel(PLB),numel(PUB)] ~= nvars)
    error('All input vectors (X0, LB, UB, PLB, PUB), if specified, need to have the same size.');
end

% Test that all vectors are row vectors
if ~isvector(x0) || ~isvector(LB) || ~isvector(UB) || ~isvector(PLB) || ~isvector(PUB) ...
        || size(x0,1) ~= 1 || size(LB,1) ~= 1 || size(UB,1) ~= 1 || size(PLB,1) ~= 1 || size(PUB,1) ~= 1
    error('All input vectors should be row vectors.');
end

% Test that plausible bounds are finite
if ~all(isfinite(([PLB, PUB])))
    error('Plausible interval bounds PLB and PUB need to be finite.');
end

% Test that all vectors are real-valued
if ~isreal([x0; LB; UB; PLB; PUB])
    error('All input vectors should be real-valued.');
end

% Fixed variables (all bounds equal)
fixidx = (x0 == LB) & (LB == UB) & (UB == PLB) & (PLB == PUB);


%ISOBJIM test if the dataset contains objects that are images
%
%  N = ISOBJIM(A)
%      ISOBJIM(A)
%
% INPUT
%  A  input dataset
%
% OUTPUT
%  N  logical value
%
% DESCRIPTION
% True if the dataset contains objects that are images. If no output is required,
% false outputs are turned into errors. This may be used for assertion.
%
% In case the objects in A should be interpreted as 1-dimensional object
% images, use  A = SETFEATSIZE(A,[1 GETFEATSIZE(A)]) for conversion.
%
% SEE ALSO (PRTools Guide)
% ISDATASET, ISDATAIM, SETFEATSIZE, GETFEATSIZE

% $Id: isobjim.m,v 1.5 2009/01/31 15:43:10 duin Exp $

function n = isobjim(a)

	n = (isa(a,'prdataset') & length(a.featsize) > 1) | isdatafile(a);

	% generate error if input is not a dataset with image data with
   % pixels being features AND no output is requested (assertion)

	if nargout == 0 & n == 0
		error([newline '---- Dataset with object images expected -----'])
	end

return


function families = compute_families(bnet)
% COMPUTE_FAMILIES
% precomputes the families of nodes in a bnet
%
% The return value is a cell array for now

ss = size(bnet.dag, 1);
families = cell(ss, 1);
for i = 1:ss
  families{i} = family(bnet.dag, i);
end


function [] = main(train_file, test_file)
    %===============================================
    %              Reading the files
    %===============================================
    [train_data_col1, train_data_col2, train_class, train_lenght] = load_data(train_file);
    [test_data_col1, test_data_col2, test_class, test_lenght] = load_data(test_file);

    %===============================================
    %                Perfrom DTW
    %===============================================
    dtw(train_data_col1, train_data_col2, train_class, train_lenght, test_data_col1, test_data_col2, test_class, test_lenght)
end