% l1dantzig_pd.m
%
% Solves
% min_x  ||x||_1  subject to  ||A'(Ax-b)||_\infty <= epsilon
%
% Recast as linear program
% min_{x,u}  sum(u)  s.t.  x - u <= 0
%                         -x - u <= 0
%             A'(Ax-b) - epsilon <= 0
%            -A'(Ax-b) - epsilon <= 0
% and use primal-dual interior point method.
%
% Usage: xp = l1dantzig_pd(x0, A, At, b, epsilon, pdtol, pdmaxiter, cgtol, cgmaxiter)
%
% x0 - Nx1 vector, initial point.
%
% A - Either a handle to a function that takes a N vector and returns a K
%     vector , or a KxN matrix.  If A is a function handle, the algorithm
%     operates in "largescale" mode, solving the Newton systems via the
%     Conjugate Gradients algorithm.
%
% At - Handle to a function that takes a K vector and returns an N vector.
%      If A is a KxN matrix, At is ignored.
%
% b - Kx1 vector of observations.
%
% epsilon - scalar or Nx1 vector of correlation constraints
%
% pdtol - Tolerance for primal-dual algorithm (algorithm terminates if
%     the duality gap is less than pdtol).
%     Default = 1e-3.
%
% pdmaxiter - Maximum number of primal-dual iterations.
%     Default = 50.
%
% cgtol - Tolerance for Conjugate Gradients; ignored if A is a matrix.
%     Default = 1e-8.
%
% cgmaxiter - Maximum number of iterations for Conjugate Gradients; ignored
%     if A is a matrix.
%     Default = 200.
%
% Written by: Justin Romberg, Caltech
% Email: jrom@acm.caltech.edu
% Created: October 2005
%

function xp = l1dantzig_pd(x0, A, At, b, epsilon, pdtol, pdmaxiter, cgtol, cgmaxiter)

largescale = isa(A,'function_handle');

if (nargin < 6), pdtol = 1e-3;  end
if (nargin < 7), pdmaxiter = 50;  end
if (nargin < 8), cgtol = 1e-8;  end
if (nargin < 9), cgmaxiter = 200;  end

N = length(x0);

alpha = 0.01;
beta = 0.5;
mu = 10;

gradf0 = [zeros(N,1); ones(N,1)];

% starting point --- make sure that it is feasible
if (largescale)
  if (max( abs(At(A(x0) - b)) - epsilon ) > 0)
    disp('Starting point infeasible; using x0 = At*inv(AAt)*y.');
    AAt = @(z) A(At(z));
    [w, cgres] = cgsolve(AAt, b, cgtol, cgmaxiter, 0);
    if (cgres > 1/2)
      disp('A*At is ill-conditioned: cannot find starting point');
      xp = x0;
      return;
    end
    x0 = At(w);
  end
else
  if (max(abs(A'*(A*x0 - b)) - epsilon ) > 0)
    disp('Starting point infeasible; using x0 = At*inv(AAt)*y.');
    opts.POSDEF = true; opts.SYM = true;
    [w, hcond] = linsolve(A*A', b, opts);
    if (hcond < 1e-14)
      disp('A*At is ill-conditioned: cannot find starting point');
      xp = x0;
      return;
    end
    x0 = A'*w;
  end
end
x = x0;
u = (0.95)*abs(x0) + (0.10)*max(abs(x0));

% set up for the first iteration
if (largescale)
  Atr = At(A(x) - b);
else
  Atr = A'*(A*x - b);
end
fu1 = x - u;
fu2 = -x - u;
fe1 = Atr - epsilon;
fe2 = -Atr - epsilon;
lamu1 = -(1./fu1);
lamu2 = -(1./fu2);
lame1 = -(1./fe1);
lame2 = -(1./fe2);
if (largescale)
  AtAv = At(A(lame1-lame2));
else
  AtAv = A'*(A*(lame1-lame2));
end

% sdg = surrogate duality gap
sdg = -[fu1; fu2; fe1; fe2]'*[lamu1; lamu2; lame1; lame2];
tau = mu*(4*N)/sdg;

% residuals
rdual = gradf0 + [lamu1-lamu2 + AtAv; -lamu1-lamu2];
rcent = -[lamu1.*fu1; lamu2.*fu2; lame1.*fe1; lame2.*fe2] - (1/tau);
resnorm = norm([rdual; rcent]);

% iterations
pditer = 0;
done = (sdg < pdtol) | (pditer >= pdmaxiter);
dispProgress('Newton', 0, pdmaxiter);
while (~done)

  % solve for step direction
  w2 = - 1 - (1/tau)*(1./fu1 + 1./fu2);

  sig11 = -lamu1./fu1 - lamu2./fu2;
  sig12 = lamu1./fu1 - lamu2./fu2;
  siga = -(lame1./fe1 + lame2./fe2);
  sigx = sig11 - sig12.^2./sig11;

  if (largescale)
    w1 = -(1/tau)*( At(A(1./fe2-1./fe1)) + 1./fu2 - 1./fu1 );
    w1p = w1 - (sig12./sig11).*w2;
    hpfun = @(z) At(A(siga.*At(A(z)))) + sigx.*z;
    [dx, cgres, cgiter] = cgsolve(hpfun, w1p, cgtol, cgmaxiter, 0);
    if (cgres > 1/2)
      disp('Cannot solve system.  Returning previous iterate.  (See Section 4 of notes for more information.)');
      xp = x;
      return
    end
    AtAdx = At(A(dx));
  else
    w1 = -(1/tau)*( A'*(A*(1./fe2-1./fe1)) + 1./fu2 - 1./fu1 );
    w1p = w1 - (sig12./sig11).*w2;
    Hp = A'*(A*sparse(diag(siga))*A')*A + diag(sigx);
    opts.POSDEF = true; opts.SYM = true;
    [dx, hcond] = linsolve(Hp, w1p,opts);
    if (hcond < 1e-14)
      disp('Matrix ill-conditioned.  Returning previous iterate.  (See Section 4 of notes for more information.)');
      xp = x;
      return
    end
    AtAdx = A'*(A*dx);
  end
  du = w2./sig11 - (sig12./sig11).*dx;

  dlamu1 = -(lamu1./fu1).*(dx-du) - lamu1 - (1/tau)*1./fu1;
  dlamu2 = -(lamu2./fu2).*(-dx-du) - lamu2 - (1/tau)*1./fu2;
  dlame1 = -(lame1./fe1).*(AtAdx) - lame1 - (1/tau)*1./fe1;
  dlame2 = -(lame2./fe2).*(-AtAdx) - lame2 - (1/tau)*1./fe2;
  if (largescale)
    AtAdv = At(A(dlame1-dlame2));
  else
    AtAdv = A'*(A*(dlame1-dlame2));
  end

  % find minimal step size that keeps ineq functions < 0, dual vars > 0
  iu1 = find(dlamu1 < 0); iu2 = find(dlamu2 < 0);
  ie1 = find(dlame1 < 0); ie2 = find(dlame2 < 0);
  ifu1 = find((dx-du) > 0); ifu2 = find((-dx-du) > 0);
  ife1 = find(AtAdx > 0); ife2 = find(-AtAdx > 0);
  smax = min(1,min([...
    -lamu1(iu1)./dlamu1(iu1); -lamu2(iu2)./dlamu2(iu2); ...
    -lame1(ie1)./dlame1(ie1); -lame2(ie2)./dlame2(ie2); ...
    -fu1(ifu1)./(dx(ifu1)-du(ifu1)); -fu2(ifu2)./(-dx(ifu2)-du(ifu2)); ...
    -fe1(ife1)./AtAdx(ife1); -fe2(ife2)./(-AtAdx(ife2)) ]));
  s = 0.99*smax;

  % backtracking line search
  suffdec = 0;
  backiter = 0;
  while (~suffdec)
    xp = x + s*dx;  up = u + s*du;
    Atrp = Atr + s*AtAdx;  AtAvp = AtAv + s*AtAdv;
    fu1p = fu1 + s*(dx-du);  fu2p = fu2 + s*(-dx-du);
    fe1p = fe1 + s*AtAdx;  fe2p = fe2 + s*(-AtAdx);
    lamu1p = lamu1 + s*dlamu1;  lamu2p = lamu2 + s*dlamu2;
    lame1p = lame1 + s*dlame1; lame2p = lame2 + s*dlame2;
    rdp = gradf0 + [lamu1p-lamu2p + AtAvp; -lamu1p-lamu2p];
    rcp = -[lamu1p.*fu1p; lamu2p.*fu2p; lame1p.*fe1p; lame2p.*fe2p] - (1/tau);
    suffdec = (norm([rdp; rcp]) <= (1-alpha*s)*resnorm);
    s = beta*s;
    backiter = backiter+1;
    if (backiter > 32)
      disp('Stuck backtracking, returning last iterate.  (See Section 4 of notes for more information.)')
      xp = x;
      return
    end
  end

  % setup for next iteration
  x = xp;  u = up;
  Atr = Atrp;  AtAv = AtAvp;
  fu1 = fu1p; fu2 = fu2p;
  fe1 = fe1p; fe2 = fe2p;
  lamu1 = lamu1p; lamu2 = lamu2p;
  lame1 = lame1p; lame2 = lame2p;

  sdg = -[fu1; fu2; fe1; fe2]'*[lamu1; lamu2; lame1; lame2];
  tau = mu*(4*N)/sdg;

  rdual = rdp;
  rcent = -[lamu1.*fu1; lamu2.*fu2; lame1.*fe1; lame2.*fe2] - (1/tau);
  resnorm = norm([rdual; rcent]);

  pditer = pditer+1;
  done = (sdg < pdtol) | (pditer >= pdmaxiter);
  dispProgress('Newton', pditer/pdmaxiter);

%   disp(sprintf('Iteration = %d, tau = %8.3e, Primal = %8.3e, PDGap = %8.3e, Dual res = %8.3e',...
%     pditer, tau, sum(u), sdg, norm(rdual)));
%   if (largescale)
%     disp(sprintf('                CG Res = %8.3e, CG Iter = %d', cgres, cgiter));
%   else
%     disp(sprintf('                  H11p condition number = %8.3e', hcond));
%   end

end
dispProgress('Newton', 'Close');
end


function C = long(d)
%LONG         Long class constructor
%
%  C = long(d)
%
%  C is long scalar or column vector, array input d forced to be column vector
%
%Long representation
%
%  C = C.sign * sum( C.mantissa(i)*beta^(-i) ) * beta^C.exponent +/- C.error
%
%where C.error = C.error.mant * beta^C.error.exp and with
%  summation from 1 to precision (=size(C.mantissa,2)) with
%
%  1 <= precision <= INTLAB_LONG_PRECISION
%
%and integers
%
%  C.sign       in {-1,1}
%  C.mantissa   in 0 .. beta-1
%  C.exponent   representable integer ( -2^52+1 .. 2^52-1 )
%  C.error      nonnegative double, stored by C.error.mant and C.error.exp
%
%Computations can be executed with or w/o error term, see longinit. For
%  computational speed, comparison, min/max and absolute value refer to
%  midpoint.
%  To compare, for example, intervals (for computation with error term)
%  use inf(A)>sup(B) instead of A>B, and so forth.
%
%For control of working precision, see help longprecision. Base beta is a
%  power of 2 so that double precision floating point numbers are stored
%  without error.
%An example of long arithmetic with big cancellation is
%
%  x = long(-20);
%  y = long(1);  t = long(1);  i = 0;
%  while abs(t)>1e-20
%    i = i+1;
%    t = t*x/i;
%    y = y+t;
%  end
%  format long
%  Y = long2intval(y)
%
%producing
%
%  intval Y =
%    1.0e-008 *
%     0.20612_________
%
%The poor accuracy improves with more precision. After
%  longprecision(40);
%and the same statements as above we obtain
%
%  intval Y =
%    1.0e-008 *
%     0.20611536224378
%
%For more information try demolong .
%

% written  12/30/98     S.M. Rump
% modified 02/09/01     S.M. Rump  performance improvement
% modified 09/29/02     S.M. Rump  care for NaN components
% modified 04/04/04     S.M. Rump  set round to nearest for safety
% modified 04/06/05     S.M. Rump  rounding unchanged
% modified 08/26/12     S.M. Rump  global variables removed
%

  INTLAB_LONG_LOGBETA = getappdata(0,'INTLAB_LONG_LOGBETA');
  INTLAB_LONG_BETA = getappdata(0,'INTLAB_LONG_BETA');
  INTLAB_LONG_ERROR = getappdata(0,'INTLAB_LONG_ERROR');

  if nargin==0
    C.sign = [];
    C.exponent = [];
    C.mantissa = [];
    C.error.mant = [];
    C.error.exp = [];
    C = class(C,'long');
    return
  end

  if isa(d,'long')
    C = d;
  else
    if isempty(d)
      C.sign = [];
      C.exponent = [];
      C.mantissa = [];
      C.error.mant = [];
      C.error.exp = [];
      C = class(C,'long');
      return
    end
    sized = size(d);
    n = prod(sized);
    if n~=sized(1)
      warning('input array for long forced to be column vector')
      d = d(:);
    end
    indexNaN = isnan(d);
    d(indexNaN) = [];
    [s e m] = splitdble(d);

    % get sign
    C.sign = s;

    % get exponent
    q = ceil(e/INTLAB_LONG_LOGBETA);
    C.exponent = q;

    % get mantissa digits
    C.mantissa = zeros(size(d,1),ceil(53/INTLAB_LONG_LOGBETA)+1);
    p = 0;
    while any(m)
      p = p+1;
      m = m*INTLAB_LONG_BETA;
      C.mantissa(:,p) = floor(m);
      m = m - C.mantissa(:,p);
    end

    % treat zero components
    index = ( d==0 );
    if any(index)
      C.sign(index) = 1;
      C.exponent(index) = -inf;
    end

    % adjust mantissa digits to exponent
    r = INTLAB_LONG_LOGBETA*q - e;
    index = ( r~=0 );
    if any(index)
      C.mantissa(index,:) = shiftright(C.mantissa(index,:),r(index));
    end

    % omit trailing zeros (improves performance)
    [m index] = max(C.mantissa(:,end:-1:1)~=0,[],2);
    index(all(C.mantissa'==0)) = size(C.mantissa,2);
    if min(index)~=1
      C.mantissa = C.mantissa(:,1:end-min(index)+1);
    end

    if any(indexNaN)
      Csign = C.sign;
      Cexponent = C.exponent;
      Cmantissa = C.mantissa;
      C.sign = zeros(n,1);
      C.exponent = zeros(n,1);
      C.mantissa = zeros(n,size(Cmantissa,2));
      C.sign(indexNaN) = NaN;
      C.exponent(indexNaN) = NaN;
      C.mantissa(indexNaN) = NaN;
      C.sign(~indexNaN) = Csign;
      C.exponent(~indexNaN) = Cexponent;
      C.mantissa(~indexNaN) = Cmantissa;
    end

    % set error
    C.error.mant = zeros(n,1);
    C.error.exp = zeros(n,1);

    C = class(C,'long');

  end


function [collapsed peakidx] = hlp_collapseFrequencies(data,collapsefun,freqidx,tindex,fspacing)
%
% Collapse matrix across frequencies. If tindex is provided, then collapse
% across frequencies for the given timeindex.
%
% Input:
%       data:           col vector or matrix of dimension [nfreqs ntimes].
%                       If matrix, collapse separately for each timepoint
%
%       collapsefun:    The method to use for collapsing across frequencies
%                       none:       no collapse
%                       integrate:  numerical integration using trapz
%                       mean:       average
%                       max:        maximum
%                       absmax:     maximum of absolute value
%                       peak:       1-dimensional peak. Returns 0 if no
%                                   peak is found
%
%       freqidx:        (opt) vector of frequency indices to collapse across
%                       If empty or omitted, use all freqs
%
%       ttindex:        (opt) vector of timepoint(s) or single timepoint
%                       If empty or omitted, use all times
%
%       fspacing:       (opt) Frequency spacing for integration. Default: 1
%
% Out:
%       collapsed:      Row vector or single containing collapsed data for
%                       each time point
%
%       peakidx:        If peak or max is used, return frequency index of
%                       peak location
%
% References:
%
% [1] Mullen T (2010) The Source Information Flow Toolbox (SIFT):
%       Theoretical Handbook and User Manual.
%       Available at: http://www.sccn.ucsd.edu/wiki/Sift/
%
%
% Author: Tim Mullen 2010, SCCN/INC, UCSD.
% Email:  tim@sccn.ucsd.edu

% This function is part of the Source Information Flow Toolbox (SIFT)
%
% 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, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

% handle defaults
if nargin < 5
    fspacing = 1;   end
if nargin < 4
    tindex  = [];   end
if nargin < 3
    freqidx = [];   end

peakidx = [];

sz    = size(data);
if length(sz)>2
    error('data cannot have more than 2 dimensions');
end

if isempty(freqidx)
    freqidx  = 1:sz(1);    end
if isempty(tindex)
    tindex   = 1:sz(2);    end

% select the desired data range
data = data(freqidx,tindex);
sz   = size(data);

if all(sz==1)
    collapsed = data;
    return;
end

% ensure we have a column vector (we always collapse for each col)
if any(sz==1), data = data(:); end

switch lower(collapsefun)
    case 'none'
        collapsed = data;
    case 'integrate'
        collapsed  = trapz(data)*fspacing;
    case 'mean'
        collapsed  = mean(data);
    case 'max'
        [collapsed peakidx]  = max(data);
    case 'peak'
         w = warning;
         warning('off','all');
         collapsed   = zeros(1,size(data,2));
         peakidx     = nan(size(collapsed));
         for t=1:size(data,2)
             val = [];
             try, [val idx] = findpeaks(data(:,t),'npeaks',1); catch, end

            if isempty(val)
                collapsed(t) = 0;
                peakidx(t)   = nan;
            else
                collapsed(t) = val;
                peakidx(t)   = idx;
            end
         end
        warning(w);
  case 'absmax'
        [collapsed peakidx]  = max(abs(data));
  case 'minmax'
        [collapsed peakidx]  = max(abs(data));
        for k=1:size(peakidx,2)
            collapsed(k) = data(peakidx(k),k);
        end
end

if isempty(collapsed)
  collapsed = 0;
  peakidx   = nan;
end


function Problem = ITERmake_CON_MAT(Problem, Constraints, Objectives, Spec)

disp(' ')
nxny = input('Number of instances of swept variables [7]: ');
if isempty(nxny); nxny = 7; end
nxny = round(nxny);
if nxny < 3; return; end
disp(' ')

tic;
nPPreqs = Spec.nPPrequirements;
Constraints.current_active_cons = Constraints.active_cons(1:nPPreqs);
if length(Constraints.active_cons)>nPPreqs
    Constraints.current_active_nonlcons = Constraints.active_cons(nPPreqs+1:end);
    Constraints.activeNonLLabels = Constraints.conLabels(nPPreqs+1:end);
    Constraints.activeNonLLabels = Constraints.activeNonLLabels(Constraints.current_active_nonlcons);
else
    Constraints.current_active_nonlcons = [];
    Constraints.activeNonLLabels = [];
end

Constraints.activePPLabels = Constraints.conLabels(1:nPPreqs);
Constraints.activePPLabels = Constraints.activePPLabels(Constraints.current_active_cons);

Constraints.nactiveMar = nnz(Constraints.current_active_cons);
Constraints.nactivenonL = nnz(Constraints.current_active_nonlcons);

Constraints.colors = [0 0 1
    0 1 0
    1 0 0
    0 1 1
    1 0 1];
while length(Constraints.colors)<Constraints.nactiveMar+Constraints.nactivenonL
    Constraints.colors = [Constraints.colors;Constraints.colors];
end
Constraints.colors = Constraints.colors(1:Constraints.nactiveMar+Constraints.nactivenonL);

activeX = Problem.activeX;
x0 = Problem.x0(activeX); %the x0 of active values;
nvars = nnz(activeX);
lb = Problem.lb(activeX);
ub = Problem.ub(activeX);
XLabels=Problem.XLabels(activeX);

if nvars < 2;
    disp('Visualizer for 2 or more decision variables')
    beep; return
end

% first get values for baseline aircraft:
disp ('Evaluating baseline aircraft...')
[Constraints.C0,~,Constraints.MarStack0,...
    Constraints.NonLMarStack0] = EVALnonlcons(x0,Problem,Constraints,Spec);
[Objectives.ObjectiveValue0, Objectives.ObjVals0] = EVALobjective...
    (x0,Problem,Objectives,Spec,Constraints,'single');
nsubplots = nvars*(nvars-1)/2;
ticker = 0;
progressbar('Constraint matrix progress' , 'Subplot sweep')
Matrix(nvars,nvars).X = [];
for ii = 1:nvars;
    for jj = 1:nvars;
        if ii~=jj %make a plot from Matrix
            if jj > ii %make a carpet plot and store it
                action = ['Generating plot ' XLabels{ii} ' versus ' XLabels{jj} '...'];
                disp(action);
                [Matrix(ii,jj).X,Matrix(ii,jj).Y,Matrix(ii,jj).ObjStack,...
                    Matrix(ii,jj).MarStack,Matrix(ii,jj).feasible,...
                    Matrix(ii,jj).feasCompObj]=...
                    carpet_plot_vals(ii,jj,...
                    x0,lb,ub,Problem, Constraints, Objectives, Spec, nxny,...
                    ticker, nsubplots);
            ticker = ticker+1;
                progressbar(ticker/nsubplots);
            elseif jj < ii %just do a reflection
                Matrix(ii,jj).Y = Matrix(jj,ii).X;
                Matrix(ii,jj).X = Matrix(jj,ii).Y;
                Matrix(ii,jj).ObjStack = Matrix(jj,ii).ObjStack;
                Matrix(ii,jj).MarStack = Matrix(jj,ii).MarStack;
                Matrix(ii,jj).feasible = Matrix(jj,ii).feasible;
            end
        end
    end
end
progressbar(1);
save
disp('Workspace saved to matlab.mat')
Tstamp = num2str(fix(clock),'%02d');
fname = ['Autosaves\CarpetMatrixData' Tstamp '.mat'];
save(fname)
toc;
beep;pause(.5);beep;pause(.5);beep;pause(.5);
beep;pause(.1);beep;pause(.1);beep;
Haxes = CMATplot_the_carpet...
    (Matrix, Constraints,Objectives,x0,200,lb,ub,XLabels);
toc;
Problem = CMATpick_point(Problem,Constraints,Spec,Objectives,Haxes);
if get_yes_or_no('Save result to History.xls? [Y]/N: ',true)
    ITERsave_hist(Problem,Constraints,Objectives,Spec,'Visualizer');
end
end

function [XX,YY,ObjStack,MarsStack,feasible,feasCompObj]=...
    carpet_plot_vals(Xyind,Xxind,x0,lb,ub,...
    Problem, Constraints, Objectives, Spec, nxny,...
    ticker, nsubplots)
% nx = 9; ny = 8;
nx = nxny;
ny = nxny;

xx = linspace3(lb(Xxind),x0(Xxind),ub(Xxind),nx);
yy = linspace3(lb(Xyind),x0(Xyind),ub(Xyind),ny);

[YY,XX]=meshgrid(yy,xx);

ObjStack = NaN(nx,ny,1+Objectives.nObj);
MarsStack = NaN(nx,ny,Constraints.nactiveMar+Constraints.nactivenonL);

waitbar1tot = nx*ny-1;
waitbar1prog = 0;
progressbar([],0)
for ii = 1:length(xx)
    for jj = 1:length(yy)

        X = x0;
        X(Xxind) = xx(ii);
        X(Xyind) = yy(jj);
        if all(X==x0) %skip evaluating the seed point
            ObjStack(ii,jj,:) = ...
                [Objectives.ObjectiveValue0; Objectives.ObjVals0(:)];
            MarsStack(ii,jj,:) = -Constraints.C0(:);
        else
            C = EVALnonlcons(X,Problem,Constraints,Spec);
            MarsStack(ii,jj,:) = -C(:);
            [ObjectiveValue, ObjVals] = EVALobjective...
                (X,Problem,Objectives,Spec,Constraints,'single');
            ObjStack(ii,jj,:) = [ObjectiveValue; ObjVals(:)];
            waitbar1prog = waitbar1prog+1;
            frac2 = waitbar1prog/waitbar1tot;
            progressbar((ticker+frac2)/nsubplots,frac2)
        end
    end
end
feasible = all(MarsStack>=0,3);
feasCompObj = ObjStack(:,:,1);
feasCompObj(~feasible) = NaN;
end


function [Bx, By, Bz] = igrf(time, latitude, longitude, altitude, coord)

% IGRF Earth's magnetic field from IGRF model.
%
% Usage: [BX, BY, BZ] = IGRF(TIME, LATITUDE, LONGITUDE, ALTITUDE, COORD)
%     or [BX, BY, BZ] = IGRF(COEFS, LATITUDE, LONGITUDE, ALTITUDE, COORD)
%     or B = IGRF(TIME, LATITUDE, LONGITUDE, ALTITUDE, COORD)
%     or B = IGRF(COEFS, LATITUDE, LONGITUDE, ALTITUDE, COORD)
%
% Calculates the components of the Earth's magnetic field using the
% International Geomagnetic Reference Field (IGRF) model. The inputs for
% the position can be scalars or vectors (in the latter case each should
% have the same number of elements or be a scalar), but TIME must be a
% scalar.
%
% When all the coordinate inputs are scalars, the function can be run more
% efficiently by providing the proper IGRF coefficient vector for a given
% time rather than the time itself. This mode is useful when making
% multiple calls to the function while keeping the time the same (meaning
% the coefficients will be the same for each run) as loading the
% coefficients can be the most time-consuming part of the function. The
% coefficient vector can be easily loaded using the function LOADIGRFCOEFS.
% This mode is assumed when all the coordinate inputs are scalars and the
% first input is a vector. In this case, the coefficient vector should be
% formatted as (LOADIGRFCOEFS provides this):
%
%   [g(n=1,m=0) g(n=1,m=1) h(n=1,m=1) g(n=2,m=0) g(n=2,m=1) h(n=2,m=1) ...]
%
% Regardless of the size of the inputs, the outputs will be column vectors.
% If only one output is requested, B = [BX(:), BY(:), BZ(:)] is output.
% Note that the other parameters the IGRF gives can be computed from BX,
% BY, and BZ as:
%
%   Horizonal intensity: hypot(BX, BY) (i.e., sqrt(BX.^2 + BY.^2) )
%   Total intensity: hypot(BX, hypot(BY, BZ))
%   Declination: atan2(BY, BX)
%   Inclination: atan(BZ./hypot(BX, BY))
%
% This function relies on having the file igrfcoefs.mat in the MATLAB
% path to function properly when a time is input. If this file cannot be
% found, this function will try to create it by calling GETIGRFCOEFS.
%
% The IGRF is a spherical harmonic expansion of the Earth's internal
% magnetic field. Currently, the IGRF model is valid between the years 1900
% and 2015. See the health warning for the IGRF model here:
% http://www.ngdc.noaa.gov/IAGA/vmod/igrfhw.html
%
% Reference:
% International Association of Geomagnetism and Aeronomy, Working Group
% V-MOD (2010), International Geomagnetic Reference Field: the eleventh
% generation, _Geophys. J. Int._, _183_(3), 1216-1230,
% doi:10.1111/j.1365-246X.2010.04804.x.
%
% Inputs:
%   -TIME: Time to get the magnetic field values either in MATLAB serial
%   date number format or a string that can be converted into MATLAB serial
%   date number format using DATENUM with no format specified (see
%   documentation of DATENUM for more information).
%   -COEFS: Instead of inputting a time, you can simply specify the proper
%   coefficients for the time you want by inputting in the first argument
%   the proper coefficient vector from igrfcoefs.mat.
%   -LATITUDE: Geocentric or geodetic latitude in degrees.
%   -LONGITUDE: Geocentric or geodetic longitude in degrees.
%   -ALTITUDE: For geodetic coordiates, the height in km above the Earth's
%   surface. For geocentric coordiates, the radius in km from the center of
%   the Earth.
%   -COORD: String specifying the coordinate system to use. Either
%   'geocentric' or 'geodetic' (optional, default is geodetic). Note that
%   only geodetic coordinates have been verified.
%
% Outputs:
%   -BX: Northward component of the magnetic field in nanoteslas (nT).
%   -BY: Eastward component of the magnetic field in nT.
%   -BZ: Downward component of the magnetic field in nT.
%   -B: [BX(:), BY(:), BZ(:)].
%
% See also: LOADIGRFCOEFS, GETIGRFCOEFS, IGRFLINE, DATENUM, IGRF11MAGM.

% Run IGRFS if all position inputs are scalars.
if isscalar(latitude) && isscalar(longitude) && isscalar(altitude)
    if nargin < 5
        [Bx, By, Bz] = igrfs(time, latitude, longitude, altitude);
    else
        [Bx, By, Bz] = igrfs(time, latitude, longitude, altitude, coord);
    end
% Otherwise run IGRFV.
else
    if nargin < 5
        [Bx, By, Bz] = igrfv(time, latitude, longitude, altitude);
    else
        [Bx, By, Bz] = igrfv(time, latitude, longitude, altitude, coord);
    end
end

if nargout <= 1
    Bx = [Bx(:), By(:), Bz(:)];
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%        IGRF vector function.        %%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [Bx, By, Bz] = igrfv(time, latitude, longitude, altitude, coord)

% Fundamental constant.
Rearth_km = 6371.2;

%%% CHECK INPUT VALIDITY %%%
% Convert time to a datenumber if it is a string.
if ischar(time)
    time = datenum(time);
end

% Make sure time has only one element.
if numel(time) > 1
    error('igrf:timeInputInvalid', ['The input TIME can only have one ' ...
        'element.']);
end

% Check that the inputs all have either one or the same number of elements.
numlat = numel(latitude);
numlon = numel(longitude);
numalt = numel(altitude);
if numlat > 1
    if numlon == 1
        longitude = repmat(longitude, size(latitude));
    end
    if numalt == 1
        altitude = repmat(altitude, size(latitude));
    end
elseif numlon > 1
    latitude = repmat(latitude, size(longitude));
    if numalt == 1
        altitude = repmat(altitude, size(latitude));
    end
elseif numalt > 1
    latitude = repmat(latitude, size(altitude));
    longitude = repmat(longitude, size(altitude));
end
numlat = numel(latitude);
numlon = numel(longitude);
numalt = numel(altitude);
if numlat ~= numlon || numlat ~= numalt || numlon ~= numalt
    error('igrf:inputNotSameSize', ['The input coordinates must have ' ...
        'the same number of elements.']);
end

%%% SPHERICAL COORDINATE CONVERSION %%%
% Convert the latitude, longitude, and altitude coordinates input into
% spherical coordinates r (radius), theta (inclination angle from +z axis),
% and phi (azimuth angle from +x axis). Also, make the coordinates go down
% the rows.
% We want cos(theta) and sin(theta) rather than theta itself.
costheta = cos((90 - latitude(:))*pi/180);
sintheta = sin((90 - latitude(:))*pi/180);

% Convert from geodetic coordinates to geocentric coordinates if necessary.
% This method was adapted from igrf11syn, which was a conversion of the
% IGRF subroutine written in FORTRAN.
if nargin < 5 || isempty(coord) || strcmpi(coord, 'geodetic') || ...
        strcmpi(coord, 'geod') || strcmpi(coord, 'gd')
    a = 6378.137; f = 1/298.257223563; b = a*(1 - f);
    rho = hypot(a*sintheta, b*costheta);
    r = sqrt( altitude(:).^2 + 2*altitude(:).*rho + ...
        (a^4*sintheta.^2 + b^4*costheta.^2) ./ rho.^2 );
    cd = (altitude(:) + rho) ./ r;
    sd = (a^2 - b^2) ./ rho .* costheta.*sintheta./r;
    oldcos = costheta;
    costheta = costheta.*cd - sintheta.*sd;
    sintheta = sintheta.*cd + oldcos.*sd;
elseif strcmpi(coord, 'geocentric') || strcmpi(coord, 'geoc') || ...
        strcmpi(coord, 'gc')
    r = altitude(:);
    cd = 1;
    sd = 0;
else
    error('igrf:coordInputInvalid', ['Unrecognized command ' coord ...
        ' for COORD input.']);
end

% Special case when sin(theta) = 0.
sintheta0 = sintheta == 0;
anysintheta0 = any(sintheta0);
anysinthetanot0 = any(~sintheta0);

% Convert longitude to radians.
phi = longitude(:)*pi/180;

%%% GET PROPER IGRF COEFFICIENTS %%%
[g, h] = loadigrfcoefs(time);
nmax = size(g, 1);

% We need cos(m*phi) and sin(m*phi) multiple times, so precalculate into a
% matrix here:
cosphi = cos(bsxfun(@times, 0:nmax, phi));
sinphi = sin(bsxfun(@times, 0:nmax, phi));

%%% BEGIN MAGNETIC FIELD CALCULATION %%%
% Initialize variables used in for loop below.
Br = zeros(size(r));
Bt = zeros(size(r));
Bp = zeros(size(r));
lastP = 1;
lastdP_1 = 0;
lastdP_2 = 0;

% Sum for each n value.
for n = 1 : nmax

    m = 0 : n;

    % Calculate legendre values. The output of the function has each m
    % value going down the rows, but since m goes along the columns
    % (coordinates go down the rows, remember?), permute it.
    P = legendre(n, costheta, 'sch').';

    % We also need the derivative of the legendre with respect to theta. It
    % is given by a recursive function of both the previous legendre values
    % as well as the previous derivatives. Functionally, it is:
    % dP(0, 0) = 0, dP(1, 1) = cos(theta)
    % dP(n, n) = sqrt(1 - 1/(2n))*(sin(theta)*dP(n-1, n-1) +
    %     cos(theta)*P(n-1, n-1))
    % dP(n, m) = (2n - 1)/sqrt(n^2 - m^2)*(cos(theta)*dP(n-1, m) -
    %     sin(theta)*P(n-1, m)) - sqrt(((n-1)^2 - m^2)/(n^2 - m^2))*
    %     dP(n-2, m)
    dP = [bsxfun(@minus, bsxfun(@times, ...
        (2*n - 1)./sqrt(n^2 - m(1:end-1).^2), ...
        bsxfun(@times, costheta, lastdP_1) - bsxfun(@times, sintheta, ...
        lastP)), bsxfun(@times, sqrt(((n - 1)^2 - m(1:end-1).^2)./...
        (n^2 - m(1:end-1).^2)), lastdP_2)), zeros(size(costheta))];
    if n > 1
        dP(:, end) = sqrt(1 - 1/(2*n))*...
            (sintheta*lastdP_1(end) + costheta*lastP(end));
        lastdP_2 = [lastdP_1 zeros(size(costheta))];
    else
        dP(:, end) = costheta;
        lastdP_2 = lastdP_1;
    end
    lastP = P;
    lastdP_1 = dP;

    % Multiply coefficients by proper longitude trigonemetric term.
    gcos = bsxfun(@times, g(n, m + 1), cosphi(:, m + 1));
    gsin = bsxfun(@times, g(n, m + 1), sinphi(:, m + 1));
    hcos = bsxfun(@times, h(n, m + 1), cosphi(:, m + 1));
    hsin = bsxfun(@times, h(n, m + 1), sinphi(:, m + 1));

    % Calculate the magnetic field components as a running sum. Find
    % explicit expressions for these in Global Earth Physics: a Handbook of
    % Physical Constants by Thomas J. Aherns (1995), pg. 49. Link:
    % http://books.google.com/books?id=aqjU_NHyre4C&lpg=PP1&dq=Global%20
    % earth%20physics%3A%20a%20handbook%20of%20physical%20constants&pg=PA49
    % #v=onepage&q&f=false
    % (except equation 6 is missing a required 1/sin(theta) and m; correct
    % equations on page 5 (equations 3a-3c) of:
    % http://hanspeterschaub.info/Papers/UnderGradStudents/
    % MagneticField.pdf)
    a_r = (Rearth_km./r).^(n + 2);
    Br = Br + a_r.*(n+1).*sum((gcos + hsin).*P, 2);
    Bt = Bt - a_r.*sum((gcos + hsin).*dP, 2);
    % Different case when sin(theta) == 0 for phi component.
    if anysinthetanot0
        Bp(~sintheta0) = Bp(~sintheta0) - 1./sintheta(~sintheta0).*...
            a_r(~sintheta0).*sum(bsxfun(@times, m, ...
            (-gsin(~sintheta0, :) + hcos(~sintheta0, :)).*...
            P(~sintheta0, :)), 2);
    end
    if anysintheta0
        Bp(sintheta0) = Bp(sintheta0) - costheta(sintheta0).*...
            a_r(sintheta0).*sum((-gsin(sintheta0, :) ...
            + hcos(sintheta0, :)).*dP(sintheta0, :), 2);
    end

end

% Convert from spherical to (x,y,z) = (North,East,Down).
Bx = -Bt;
By = Bp;
Bz = -Br;

% Convert back to geodetic coordinates if necessary.
Bx_old = Bx;
Bx = Bx.*cd + Bz.*sd;
Bz = Bz.*cd - Bx_old.*sd;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%        IGRF scalar function.        %%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [Bx, By, Bz] = igrfs(time, latitude, longitude, altitude, coord)

% Fundamental constant.
Rearth_km = 6371.2;

%%% CHECK INPUT VALIDITY %%%
% Convert time to a datenumber if it is a string.
if ischar(time)
    time = datenum(time);
end

% Check that the input coordinates are scalars.
if ~isscalar(latitude) || ~isscalar(longitude) || ~isscalar(altitude)
    error('igrf1:inputNotScalar', ...
        'The input coordinates must be scalars.');
end

%%% SPHERICAL COORDINATE CONVERSION %%%
% Convert the latitude, longitude, and altitude coordinates input into
% spherical coordinates r (radius), theta (inclination angle from +z axis),
% and phi (azimuth angle from +x axis).
% We want cos(theta) and sin(theta) rather than theta itself.
costheta = cos((90 - latitude)*pi/180);
sintheta = sin((90 - latitude)*pi/180);

% Convert from geodetic coordinates to geocentric coordinates if necessary.
% This method was adapted from igrf11syn, which was a conversion of the
% IGRF subroutine written in FORTRAN.
if nargin < 5 || isempty(coord) || strcmpi(coord, 'geodetic') || ...
        strcmpi(coord, 'geod') || strcmpi(coord, 'gd')
    a = 6378.137; f = 1/298.257223563; b = a*(1 - f);
    rho = hypot(a*sintheta, b*costheta);
    r = sqrt( altitude.^2 + 2*altitude.*rho + ...
        (a^4*sintheta.^2 + b^4*costheta.^2) ./ rho.^2 );
    cd = (altitude + rho) ./ r;
    sd = (a^2 - b^2) ./ rho .* costheta.*sintheta./r;
    oldcos = costheta;
    costheta = costheta.*cd - sintheta.*sd;
    sintheta = sintheta.*cd + oldcos.*sd;
elseif strcmpi(coord, 'geocentric') || strcmpi(coord, 'geoc') || ...
        strcmpi(coord, 'gc')
    r = altitude;
    cd = 1;
    sd = 0;
else
    error('igrf:coordInputInvalid', ['Unrecognized command ' coord ...
        ' for COORD input.']);
end

% Convert longitude to radians.
phi = longitude*pi/180;

%%% GET PROPER IGRF COEFFICIENTS %%%
if isscalar(time)
    gh = loadigrfcoefs(time);
    nmax = sqrt(numel(gh) + 1) - 1;
% Assume a vector input means the coefficients are the input.
else
    gh = time;
    nmax = sqrt(numel(gh) + 1) - 1;
    % nmax should be an integer.
    if nmax - round(nmax) ~= 0
        error('igrf:timeInputInvalid', ['TIME input should either be ' ...
            'a single date or a valid coefficient vector.']);
    end
end

% We need cos(m*phi) and sin(m*phi) multiple times, so precalculate into a
% vector here:
cosphi = cos((1:nmax)*phi);
sinphi = sin((1:nmax)*phi);

Pmax = (nmax+1)*(nmax+2)/2;

%%% BEGIN MAGNETIC FIELD CALCULATION %%%
% Initialize variables used in for loop below.
Br = 0; Bt = 0; Bp = 0;
 P = zeros(1, Pmax);  P(1) = 1;  P(3) = sintheta;
dP = zeros(1, Pmax); dP(1) = 0; dP(3) = costheta;

% For this initial condition, the first if will result in n = 1, m = 0.
m = 1; n = 0; coefindex = 1;

a_r = (Rearth_km/r)^2;

% Increment through all the n's and m's. gh will be a vector with g
% followed by h for incrementing through n and m except when h would be
% redundant (i.e., when m = 0).
for Pindex = 2:Pmax

    % Increment to the next n when m becomes larger than n.
    if n < m
        m = 0;
        n = n + 1;
        a_r = a_r*(Rearth_km/r); % We need (Rearth_km./r)^(n+2)
    end

    % Calculate P and dP. They are given recursively according to:
    %
    % P(0, 0) = 1, P(1, 1) = sin(theta) <- Specified above
    % P(n, n) = sqrt(1 - 1/(2n))*sin(theta)*P(n-1, n-1)
    % P(n, m) = (2n - 1)/sqrt(n^2 - m^2)*cos(theta)*P(n-1, m) -
    %     sqrt(((n-1)^2 - m^2) / (n^2 - m^2)) * P(n-2, m)
    %
    % dP(0, 0) = 0, dP(1, 1) = cos(theta) <- Specified above
    % dP(n, n) = sqrt(1 - 1/(2n))*(sin(theta)*dP(n-1, n-1) +
    %     cos(theta)*P(n-1, n-1))
    % dP(n, m) = (2n - 1)/sqrt(n^2 - m^2)*(cos(theta)*dP(n-1, m) -
    %     sin(theta)*P(n-1, m)) - sqrt(((n-1)^2 - m^2)/(n^2 - m^2))*
    %     dP(n-2, m)
    if m < n && Pindex ~= 3 % (Pindex=3 is n=1, m=1, initial cond. above)
        last1n = Pindex - n;
        last2n = Pindex - 2*n + 1;
        P(Pindex) = (2*n - 1)/sqrt(n^2 - m^2)*costheta*P(last1n) - ...
            sqrt(((n-1)^2 - m^2) / (n^2 - m^2)) * P(last2n);
        dP(Pindex) = (2*n - 1)/sqrt(n^2 - m^2)*(costheta*dP(last1n) - ...
            sintheta*P(last1n)) - sqrt(((n-1)^2 - m^2) / (n^2 - m^2)) * ...
            dP(last2n);
    elseif Pindex ~= 3
        lastn = Pindex - n - 1;
        P(Pindex) = sqrt(1 - 1/(2*m))*sintheta*P(lastn);
        dP(Pindex) = sqrt(1 - 1/(2*m))*(sintheta*dP(lastn) + ...
            costheta*P(lastn));
    end

    % Calculate the magnetic field components as a running sum. Find
    % explicit expressions for these in Global Earth Physics: a Handbook of
    % Physical Constants by Thomas J. Aherns (1995), pg. 49. Link:
    % http://books.google.com/books?id=aqjU_NHyre4C&lpg=PP1&dq=Global%20
    % earth%20physics%3A%20a%20handbook%20of%20physical%20constants&pg=PA49
    % #v=onepage&q&f=false
    % (except equation 6 is missing a required 1/sin(theta) and m; correct
    % equations on page 5 (equations 3a-3c) of:
    % http://hanspeterschaub.info/Papers/UnderGradStudents/
    % MagneticField.pdf)
    if m == 0 % Implies h = 0, so only coefficient in gh is g
        coef = a_r*gh(coefindex); %*cos(0*phi) = 1
        Br = Br + (n+1)*coef*P(Pindex);
        Bt = Bt - coef*dP(Pindex);
        % Bp is 0 for m = 0.
        coefindex = coefindex + 1; % Only need to skip over g this time.
    else
        coef = a_r*(gh(coefindex)*cosphi(m) + gh(coefindex+1)*sinphi(m));
        Br = Br + (n+1)*coef*P(Pindex);
        Bt = Bt - coef*dP(Pindex);
        if sintheta == 0 % Use different formula when dividing by 0.
            Bp = Bp - costheta*a_r*(-gh(coefindex)*sinphi(m) + ...
                gh(coefindex+1)*cosphi(m))*dP(Pindex);
        else
            Bp = Bp - 1/sintheta*a_r*m*(-gh(coefindex)*sinphi(m) + ...
                gh(coefindex+1)*cosphi(m))*P(Pindex);
        end
        coefindex = coefindex + 2; % Skip over g and h this time.
    end

    % Increment m.
    m = m + 1;

end

% Convert from spherical to (x,y,z) = (North,East,Down).
Bx = -Bt;
By = Bp;
Bz = -Br;

% Convert back to geodetic coordinates if necessary.
Bx_old = Bx;
Bx = Bx.*cd + Bz.*sd;
Bz = Bz.*cd - Bx_old.*sd;


%FEATSELM Feature selection map
%
% [W,R] = FEATSELM(A,CRIT,METHOD,K,T,PAR1,...)
%
% INPUT
%   A      	Training dataset
%   CRIT   	Name of criterion: 'in-in', 'maha-s', 'NN' or others
%           (see FEATEVAL) or an untrained classifier V (default: 'NN')
%   METHOD  - 'forward' : selection by featself (default)
% 	        - 'float'   : selection by featselp
% 	        - 'backward': selection by featselb
% 	        - 'b&b'     : branch and bound selection by featselo
% 	        - 'ind'     : individual
% 	        - 'lr'      : plus-l-takeaway-r selection by featsellr
%           - 'sparse'  : use sparse untrained classifier CRIT
%   K      	Desired number of features (default: K = 0, return optimal set)
%   T      	Tuning set to be used in FEATEVAL (optional)
%   PAR1,.. Optional parameters:
% 	        - L,R       : for 'lr' (default: L = 1, R = 0)
%
% OUTPUT
%   W       Feature selection mapping
%   R       Matrix with step by step results
%
% DESCRIPTION
% Computation of a mapping W selecting K features. This routines offers a
% central interface to all other feature selection methods. W can be used
% for selecting features in a dataset B using B*W.
%
% SEE ALSO (PRTools Guide)
% MAPPINGS, DATASETS, FEATEVAL, FEATSELO, FEATSELB, FEATSELI,
% FEATSELP, FEATSELF, FEATSELLR

% Copyright: R.P.W. Duin, r.p.w.duin@37steps.com
% Faculty EWI, Delft University of Technology
% P.O. Box 5031, 2600 GA Delft, The Netherlands

function [w,res] = featselm(a,crit,arg3,ksel,t,par1,par2)

	if (nargin < 2 | isempty(crit))
		prwarning(2,'criterion not specified, assuming NN');
		crit = 'NN';
	end
	if (nargin < 3 | isempty(arg3))
		prwarning(2,'method not specified, assuming forward');
		arg3 = 'forward';
	end
	if (nargin < 4)
		ksel = [];
	end
	if (nargin < 5)
		prwarning(3,'no tuning set supplied (risk of overfit)');
		t = [];
	end
	if (nargin < 6), par1 = []; end;
	if (nargin < 7), par2 = []; end;

	% If no arguments are supplied, return an untrained mapping.

	if (nargin == 0) | (isempty(a))
		w = prmapping('featselm',{crit,arg3,ksel,t,par1,par2});
		w = setname(w,'Feature Selection');
		return
	end

	a = testdatasize(a);
	[m,k] = size(a);

	if (isstr(arg3))
		method = arg3;												% If the third argument is a string,
		switch (method)												%   it specifies the method to use.
		 case {'forward','featself'}
		  [w,res] = featself(a,crit,ksel,t);
		 case {'float','featselp'}
		  [w,res] = featselp(a,crit,ksel,t);
		 case {'backward','featselb'}
		  [w,res] = featselb(a,crit,ksel,t);
		 case {'b&b','featselo'}
		  [w,res] = featselo(a,crit,ksel,t);
		 case {'ind','featseli'}
		  [w,res] = featseli(a,crit,ksel,t);
		 case {'lr','featsellr'}
		  [w,res] = featsellr(a,crit,ksel,par1,par2,t);
		 case {'sparse'}
		  v = a*crit;
		  if isaffine(v)
		  	v = getdata(v,'rot');
		  	w = featsel(size(a,2),find(v(:,1) == 0));
			else
				v = getdata(v,'beta');
		  end
		  w = featsel(size(a,2),find(v(:,1) ~= 0));
		 otherwise
		  error('Unknown method specified.')
		end
	elseif (ismapping(arg3))
		w = arg3;												% If the third argument is a mapping,
		isuntrained(w);									%  assert it is untrained and train
		[w,res] = feval(mfilename,a,crit,w.mapping_file,ksel,t,par1,par2);
	else
		error('Illegal method specified.')
	end

return


function ti = treeinfo(X, y, treeopts, tstopts)
%TREEINFO Collects decision tree information based on tree walking.
%
%   ti = treeinfo(X, y, treeopts, tstopts)
%
%   It creates CART according to data set [X, y] and
%   optional parameters in treeopts, performs
%   tree pruning (if required) according to optional
%   parameters in tstopts, and implements tree walking
%   to collect all necessary information for genfis4.
%   treeopts and tstopts should be either empty matrix or
%   cell array containing one or more optional
%   parameter name/value pairs.
%
%   The result is a structure ti containing the following fields:
%   - structure array "node" with fields "variable" (integer-valued scalar),
%       "cutpoint" (float scalar), and "sample" (index-valued vector);
%   - structure array "leaf" with field "sample" (index-valued vector);
%   - structure array "branch" with fields "nodes" (integer-valued vector),
%       and "ineqs" (vector of "1" or "-1" values).
%
%   It requires Statistics Toolbox realizing CART algorithm.
%
%   Example:
%   load fisheriris;
%   treeopts = {'minparent', 5, 'prune', 'off'};
%   tstopts = {'crossvalidation', meas, species};
%   ti = treeinfo(meas, species, treeopts, tstopts);

%   Per Konstantin A. Sidelnikov, 2009.

% Check inputs
error(nargchk(2, 4, nargin));

if (nargin < 4) || isempty(tstopts)
    tstopts = {};
end
if (nargin < 3) || isempty(treeopts)
    treeopts = {};
end

% Create a decision tree using CART algorithm
t = classregtree(X, y, treeopts{:});
% If necessary, perfom optimal tree pruning
if ~isempty(tstopts)
    [~, ~, ~, bestlevel] = t.test(tstopts{:});
    t = t.prune('level', bestlevel);
end
% Get indices of nodes and leaves (terminal nodes)
indn = find(t.isbranch());
indl = find(~t.isbranch());
% Get overall numbers of nodes and leaves
numn = length(indn);
numl = length(indl);
% Allocate memory for structure array,
% which stores information about each branch nodes:
% cut variable number, cut point, and observations from
% the original data that satisfy the conditions for the node
node(numn).variable = [];
node(numn).cutpoint = [];
node(numn).sample = [];
% Allocate memory for structure array,
% which stores information about each leaves:
% observations from the original data that
% satisfy the conditions for the leaf
leaf(numl).sample = [];
% Allocate memory for structure array,
% which stores information about each branches:
% group of nodes that form a path to the leaf, and
% types of inequalities that are satisfied to reach the leaf
branch(numl).nodes = [];
branch(numl).ineqs = [];
% Implement tree walking
treewalk(1, [], [], 1 : length(y));
% Collect tree information
ti = struct('node', node, 'leaf', leaf, 'branch', branch);

    function treewalk(n, nodes, ineqs, sample)
        %   This nested function implements tree walking
        %   based on recursive algorithm in reference and
        %   collects tree information using data sharing.

        %   Reference: Cormen T.H., Leiserson C.E., Rivest R.L.,
        %   Introduction to Algorithms (1st ed.),
        %   MIT Press and McGraw-Hill, 1990.

        % Get numbers of the child nodes
        kids = t.children(n);
        if all(kids) % branch node
            % Get cut variable and cut point
            var = t.var(n);
            cut = t.cutpoint(n);
            % Save cut variable, cut point, and observations
            k = find(n == indn, 1, 'first');
            node(k).variable = var;
            node(k).cutpoint = cut;
            node(k).sample = sample;
            % Form new subsample and continue walking to the left child
            s = sample(X(sample, var) < cut);
            treewalk(kids(1), [nodes, k], [ineqs, -1], s);
            % Form new subsample and continue walking to the right child
            s = sample(X(sample, var) >= cut);
            treewalk(kids(2), [nodes, k], [ineqs, 1], s);
        else % leaf
            % Save leaf and branch information
            k = find(n == indl, 1, 'first');
            leaf(k).sample = sample;
            branch(k).nodes = nodes;
            branch(k).ineqs = ineqs;
        end
    end
end


% Filtering A Delta Function Input Signal Example Part 2
%
% This example illustrates how numerical aliasing can be avoided by
% spatially smoothing the source mask.
%
% author: Bradley Treeby
% date: 19th January 2010
% last update: 24th August 2014
%
% This function is part of the k-Wave Toolbox (http://www.k-wave.org)
% Copyright (C) 2009-2014 Bradley Treeby and Ben Cox

% This file is part of k-Wave. k-Wave is free software: you can
% redistribute it and/or modify it under the terms of the GNU Lesser
% General Public License as published by the Free Software Foundation,
% either version 3 of the License, or (at your option) any later version.
%
% k-Wave 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 Lesser General Public License for
% more details.
%
% You should have received a copy of the GNU Lesser General Public License
% along with k-Wave. If not, see <http://www.gnu.org/licenses/>.

clear all

% =========================================================================
% SIMULATION
% =========================================================================

% create the computational grid
Nx = 256;           % number of grid points in the x (row) direction
dx = 10e-3/Nx;      % grid point spacing in the x direction [m]
kgrid = makeGrid(Nx, dx);

% define the properties of the propagation medium
medium.sound_speed = 1500;  % [m/s]

% create a time array
num_time_steps = 1024;
[kgrid.t_array, dt] = makeTime(kgrid, medium.sound_speed);
kgrid.t_array = 0:dt:dt*(num_time_steps - 1);

% define a single element source
source_offset = 50;
source.p_mask = zeros(Nx, 1);
source.p_mask(1 + source_offset, 1) = 1;

% spatially smooth the source mask maintaining the maximum magnitude
source.p_mask = smooth(kgrid, source.p_mask, true);

% threshold out small values
source.p_mask(source.p_mask < 0.05) = 0;

% define a delta function input pulse
temporal_offset = 100;      % [time steps]
source_magnitude = 2;       % [au]
source_func = zeros(size(kgrid.t_array));
source_func(temporal_offset) = source_magnitude;

% assign and scale the input pulse based on the source mask
source.p(1:sum(source.p_mask ~= 0),:) = source.p_mask(source.p_mask ~= 0)*source_func;

% force the source mask to be binary
source.p_mask(source.p_mask ~= 0) = 1;

% define a single element sensor
sensor.mask = zeros(Nx, 1);
sensor.mask(end - source_offset, 1) = 1;

% run the simulation
sensor_data = kspaceFirstOrder1D(kgrid, medium, source, sensor, 'PMLSize', 30);

% compute the amplitude spectra of the recorded time series
[f, output_as] = spect(sensor_data, 1/dt);

% extract the maximum frequency supported by the grid (two points per
% wavelength)
f_max = kgrid.k_max * min(medium.sound_speed(:)) / (2*pi);

% =========================================================================
% VISUALISATION
% =========================================================================

% plot the input and recorded time series
figure;
[t_sc, scale, prefix] = scaleSI(max(kgrid.t_array(:)));
plot(kgrid.t_array*scale, source.p(floor(sum(source.p_mask(:))/2) + 1, :), 'k-', kgrid.t_array*scale, sensor_data, 'b-');
xlabel(['Time [' prefix 's]']);
ylabel('Pressure [au]');
legend('input pulse', 'recorded pulse');

% plot the amplitude spectrum
[f_sc, scale, prefix] = scaleSI(max(f));
figure;
plot(f*scale, output_as, 'b-');
xlabel(['Frequency [' prefix 'Hz]']);
ylabel('Amplitude [au]');

% plot the maximum frequency supported by the grid
ylim = get(gca, 'YLim');
hold on;
line([f_max*scale, f_max*scale], [0, ylim(2)], 'LineStyle','--', 'Color', 'k');
legend('amplitude spectrum of recorded pulse', 'maximum frequency supported by grid');


function [x,value] = steepestDescent(fun,x,varargin)
% find maximum with steepest descent
%
% Input
%  fun - @S2Fun, @SO3Fun
%  x   - initial seed @vector3d, @rotation
%
% Output
%  x     - local maxima
%  value - function values
%
% Options
%  maxIter    -
%  resolution -

% prepare stepsize computation by Armijo search
maxIter = get_option(varargin,'maxIter',30);
res = get_option(varargin,'resolution',0.05*degree);
omega = 1.25.^(-30:1:10) * degree;
omega(omega<res) = [];
omega = [0,omega];

% use fixed number of iterations
for k = 1:maxIter

  % compute gradient - element of tangential space
  g = normalize(fun.grad(x));

  % prepare for linesearch
  line_x = exp(repmat(x(:),1,length(omega)),g(:) * omega);

  % evaluate along lines
  line_v = fun.eval(line_x);

  % find maxima along line
  [value,id] = max(line_v,[],2);

  % update nodes
  x = line_x(sub2ind(size(line_x),(1:length(x)).',id));

  % if there was no change -> abbort
  if all(id == 1), break; end
  %fprintf('.')
end

% [o2,v1,v2] = unique(ori)
% v = accumarray(v2,1)
%id = v>5;
%o2 = o2(id);
%v = v(id)


function [llh] = tapas_sem_prosa_llh(y, u, theta, ptheta)
%% Computes the likelihood of the data.
%
% Input
%
%   y -- Observed behavioral data. A structure with fields 't' times and 'a'
%       action
%   u -- Experimental input. A structure with fields: 'tt' trial type, either
%       prosaccade or antisaccade
%   theta -- Model parameters
%   ptheta -- Priors
%
% Output
%
%   llh -- Log likelilhood
%
% aponteeduardo@gmail.com
% copyright (C) 2015
%

% Compute the likelihood of antisaccades and prosaccades

llh = zeros(1, numel(theta));

it = y.i;

method = ptheta.method;
ptrans = ptheta.ptrans;

for i = 1:numel(theta)
    llh(i) = sum(tapas_sem_prosa_cllh(y.t(~it), y.a(~it), u.tt(~it), ...
        ptrans(theta{i}), method, 1), 1);
end

end


function result = blur(im,levels,filt)
% Blurs an image by blurring and subsampling repeatedly, followed by
% upsampling and blurring.
%
%      result=blur(im,[levels],[filt])
%
%  im     -  input image.
%  levels -  number of times to blur and subsample (default is 1).
%  filt   -  blurring 1d filter to be applied separably to the rows and
%            cols of im (default ='binom5').
%
% HISTORY:
%
% DJH '96
% update 12/97 to conform to Eero's updated pyrTools
%
% I don't think we should use this any more.  Why not just use Matlab's
% convolution or blurring or filtering? (BW)
% Because this is routine *much* faster than convolution. (RFD)
%
% 2007.05.03 RFD: fixed non-double class support.

% warning('This function calls very old mex files that might not work. Try imblur instead.');

if ~exist('levels','var'), levels=1; end
if ~exist('filt','var'),   filt = 'binom5'; end
if ischar(filt),           filt = namedFilter(filt); end

% ras 03/07: enforce double-precision data type
% (the compiled corrDn functions need double)
if ~isa(im, 'double'), type = class(im); im = double(im); end
tmp = blurDn(im,levels,filt);

% save upBlurDEBUG tmp levels filt
result = upBlur(tmp,levels,filt);

% Make sure its the same size as the input image
result = result((1:size(im,1)),(1:size(im,2)));

if exist('type', 'var')
	% we had a non-double matrix, convert back
	result = feval(type, result);
end

return;


function centroid = findBallFcn(greenBall1, thresh, imageType, axH)

% Copyright 2011 The MathWorks, Inc.

error(nargchk(3, 4, nargin, 'struct'));

if isempty(greenBall1)
    return;
end
if ischar(greenBall1)
    greenBall1 = imread(greenBall1);
end

%% Find Green Object
% This script reads in an image file and then attempts to find a green
% object in the image. It is designed to find one green ball and highlight
% that ball on the original image

%% Extract each color
% Next we using indexing to extract three 2D matrices from the 3D image
% data corresponding to the red, green, and blue components of the image.

r = greenBall1(:, :, 1);
g = greenBall1(:, :, 2);
b = greenBall1(:, :, 3);

%% Calculate Green
% Then we perform an arithmetic operation on the matrices as a whole to try
% to create one matrix that represents an intensity of green.

justGreen = g - r/2 - b/2;

%% Threshold the image
% Now we can set a threshold to separate the parts of the image that we
% consider to be green from the rest.

if nargin == 4
    bw = justGreen > thresh;
else
    bw = justGreen > 80;
end

%% Remove small groups
% We can use special functions provided by the Image Processing toolbox to
% quickly perform common image processing tasks. Here we are using
% BWAREAOPEN to remove groups of pixels less than 30.

ball1 = bwareaopen(bw, 30);

%% Find center
% Now we are using REGIONPROPS to extract the centroid of the group of
% pixels representing the ball.

s  = regionprops(ball1, {'centroid','area'});
if isempty(s)
    centroid = [];
else
    [maxArea, id] = max([s.Area]); %#ok<ASGLU>
    centroid = s(id).Centroid;
end
switch imageType
   case 'video'
      imshow(greenBall1, 'Parent', axH);
      if ~isempty(centroid)
         line(centroid(1), centroid(2), 'Parent', axH, 'Color', 'w', 'Marker', 'p', 'MarkerSize', 20, 'MarkerFaceColor', 'r')
      end
   case 'bw'
      imshow(ball1, 'Parent', axH);
end


function test_suite=test_montecarlo_phase_stat
% tests for test_phase_stat
%
% #   For CoSMoMVPA's copyright information and license terms,   #
% #   see the COPYING file distributed with CoSMoMVPA.           #
    try % assignment of 'localfunctions' is necessary in Matlab >= 2016
        test_functions=localfunctions();
    catch % no problem; early Matlab versions can use initTestSuite fine
    end
    initTestSuite;

function r=randint()
    r=ceil(rand()*10+10);

function test_phase_stat_basics
    ds=generate_random_phase_dataset(40+randint(),'small');
    nsamples=size(ds.samples,1);

    methods={'param','nonparam_nan','nonparam',''};
    outputs={'pbi','pos','pop'};

    for k=1:numel(outputs)
        method=methods{k};
        for j=1:numel(outputs)
            output=outputs{j};

            opt=struct();
            opt.niter=randint();
            opt.progress=false;

            opt.permuter_func=@(iter) deterministic_permute(nsamples,...
                                                        opt.niter,iter);
            opt.output=output;
            opt.seed=randint();

            is_parametric=false;
            extreme_tail_is_nan=true;
            switch method
                case 'param'
                    opt.zscore='parametric';
                    is_parametric=true;

                case 'nonparam_nan'
                    opt.zscore='non_parametric';

                case 'nonparam'
                    opt.extreme_tail_set_nan=false;
                    extreme_tail_is_nan=false;

                case ''
                    % deafults,ok

                otherwise
                    assert(false);
            end

            expected_samples=compute_expected_samples(ds,output,...
                                        opt.niter,opt.permuter_func,...
                                        is_parametric,...
                                        extreme_tail_is_nan);
            result=cosmo_montecarlo_phase_stat(ds,opt);

            assertElementsAlmostEqual(expected_samples,result.samples,...
                                            'absolute',1e-5);
        end
    end

function test_random_data_nonparam_uniformity
% when getting phase stats for random data, z-scores must follow some sort
% of z-like distribution
    ds=generate_random_phase_dataset(40+randint(),'big');
    nsamples=size(ds.samples,1);

    methods={'nonparam_nan','nonparam',''};
    outputs={'pbi','pos','pop'};
    for k=1:numel(outputs)
        method=methods{k};
        for j=1:numel(outputs)
            output=outputs{j};

            opt=struct();
            opt.niter=50+randint();
            opt.output=output;
            opt.seed=[];
            opt.permuter_func=@(unused)nondeterministic_permute(nsamples);
            opt.progress=false;

            if ~isempty(method)
                opt.zscore='non_parametric';
                opt.extreme_tail_set_nan=~strcmp(method,'nonparam');
            end

            stat_ds=cosmo_montecarlo_phase_stat(ds,opt);
            samples=stat_ds.samples;
            nan_msk=isnan(samples);
            assert(mean(nan_msk)<.2); % not too many nans

            z_sorted=sort(samples(~nan_msk));
            n_z=numel(z_sorted);

            p_uniform=(.5:n_z)/n_z;
            z_uniform=cosmo_norminv(p_uniform);

            r2=var(z_sorted);
            r2_resid=var(z_sorted-z_uniform);
            F=r2/r2_resid;
            assert(F>10);
        end
    end

function ds=generate_random_phase_dataset(nsamples_per_class,size_str)
    ds=cosmo_synthetic_dataset('ntargets',2,...
                                    'nchunks',nsamples_per_class,...
                                    'size',size_str,...
                                    'seed',0);
    sz=size(ds.samples);
    ds.samples=randn(sz)+1i*randn(sz);
    ds.sa.chunks(:)=1:sz(1);

function samples=compute_expected_samples(ds,output,...
                                        niter,permuter_func,...
                                        is_parametric,...
                                        extreme_tail_is_nan)

    stat_orig=cosmo_phase_stat(ds,'output',output);
    [nsamples,nfeatures]=size(ds.samples);

    stat_null_cell=cell(niter,1);
    for iter=1:niter
        rp=permuter_func(iter);
        ds_null=ds;
        ds_null.sa.targets=ds.sa.targets(rp);
        stat=cosmo_phase_stat(ds_null,'output',output);
        stat_null_cell{iter}=stat;
    end

    stat_null=cosmo_stack(stat_null_cell);

    if is_parametric
        mu=mean(stat_null.samples,1);
        sd=std(stat_null.samples,[],1);

        samples=(stat_orig.samples-mu)./sd;
    else
        count_gt=sum(bsxfun(@gt,stat_orig.samples,stat_null.samples),1);
        count_lt=sum(bsxfun(@lt,stat_orig.samples,stat_null.samples),1);

        msk_gt=count_gt>niter/2;
        msk_lt=count_lt>niter/2;

        p=zeros(1,nfeatures)+.5;
        p(msk_gt)=count_gt(msk_gt)/(1+niter);
        p(msk_lt)=1-count_lt(msk_lt)/(1+niter);

        min_p=1/(1+niter)+1e-10;

        assert(all(p>=min_p-2e-10));
        assert(all((1-p)>=(min_p-2e-10)));

        if extreme_tail_is_nan
            p(count_gt==niter | count_lt==niter)=NaN;
        end

        samples=cosmo_norminv(p);
    end

function func=get_determistic_permute_func(ntargets,niter)
    func=@(iter) deterministic_permute(ntargets,niter,iter);

function targets_idxs=deterministic_permute(ntargets,niter,iter)
    persistent cached_rand_vec;
    persistent cached_args;

    args={ntargets,niter};

    if ~isequal(args,cached_args)
        cached_rand_vec=cosmo_rand(ntargets,1,'seed',ntargets*niter);

        cached_args=args;
    end

    rand_vals=cached_rand_vec+iter/niter;
    msk=rand_vals>1;
    rand_vals(msk)=rand_vals(msk)-1;

    [unused,targets_idxs]=sort(rand_vals,1);

function target_idxs=nondeterministic_permute(ntargets)
    rand_vals=randn(ntargets,1);
    [unused,target_idxs]=sort(rand_vals);

function test_monte_carlo_phase_stat_seed
    ds=generate_random_phase_dataset(20,'tiny');

    opt=struct();
    opt.niter=10+randint();
    opt.output='pbi';
    opt.progress=false;

    % different results with empty seeed
    opt.seed=[];
    r1=cosmo_montecarlo_phase_stat(ds,opt);
    attempt=10;
    while attempt>0
        attempt=attempt-1;
        assert(attempt>0,'results are always the same');
        r2=cosmo_montecarlo_phase_stat(ds,opt);
        if ~isequal(r1.samples,r2.samples)
            break;
        end
    end

    % fixed seed, same result
    opt.seed=randint();
    r1=cosmo_montecarlo_phase_stat(ds,opt);
    r2=cosmo_montecarlo_phase_stat(ds,opt);
    assertElementsAlmostEqual(r1.samples,r2.samples);

    % different seed, different result
    attempt=10;
    while attempt>0
        opt.seed=opt.seed+1;
        attempt=attempt-1;
        assert(attempt>0,'results are always the same');
        r2=cosmo_montecarlo_phase_stat(ds,opt);
        if ~isequal(r1.samples,r2.samples)
            break;
        end
    end

function test_montecarlo_phase_stat_exceptions()
    func=@cosmo_montecarlo_phase_stat;
    aet=@(x,varargin)assertExceptionThrown(@()...
                func(x,varargin{:}),'');
    extra_args=cosmo_structjoin({'progress',false,...
                                'niter',3,...
                                'output','pbi'});
    aet_arg=@(x,varargin)aet(x,extra_args,varargin{:});

    % valid
    ds=generate_random_phase_dataset(5,'tiny');
    func(ds,extra_args); % ok

    % unbalanced targets
    bad_ds=ds;
    i=find(ds.sa.targets==2,1,'first');
    bad_ds.sa.targets(i)=1;
    aet_arg(bad_ds);

    % invalid output
    aet_arg(ds,'output','foo');

    % valid zscore
    func(ds,extra_args,'zscore','parametric');
    func(ds,extra_args,'zscore','non_parametric');

    % invalid zscore
    aet_arg(ds,'zscore','nonparametric');
    aet_arg(ds,'zscore','foo');

    % invalid niter
    aet_arg(ds,'niter',.3);
    aet_arg(ds,'niter',-3);
    aet_arg(ds,'niter',[2 2]);
    aet_arg(ds,'niter','f');

    % valid output
    func(ds,extra_args,'output','pbi');
    func(ds,extra_args,'output','pos');
    func(ds,extra_args,'output','pop');

    % invalid output
    aet_arg(ds,'output','foo');
    aet_arg(ds,'output',2);

    % missing fields
    aet(ds,rmfield(extra_args,'niter'));
    aet(ds,rmfield(extra_args,'output'));

    % valid extreme_tail_set_nan
    func(ds,extra_args,'extreme_tail_set_nan',true);
    func(ds,extra_args,'extreme_tail_set_nan',false);

    % invalid func(ds,extra_args,'extreme_tail_set_nan',true);
    aet(ds,extra_args,'extreme_tail_set_nan',2);
    aet(ds,extra_args,'extreme_tail_set_nan','foo');

function test_unit_length_exception
    ds=cosmo_synthetic_dataset('nchunks',10);
    sample_size=size(ds.samples);
    ds.sa.chunks(:)=1:sample_size(1);

    rand_func=single(randn(sample_size));

    ds.samples=rand_func() + 1i*rand_func();

    opt=struct();
    opt.output='pos';
    opt.niter=100;
    opt.progress=false;

    % should not raise an exception
    cosmo_montecarlo_phase_stat(ds,opt);


function M = ptv_params_to_matrix(params, transform_type)
    if isnumeric(transform_type)
        if transform_type == -1
            transform_type = 'translate';
        elseif transform_type == -2
            transform_type = 'rigid';
        elseif transform_type == -3
            transform_type = 'rigid_scale';
        elseif transform_type == -4
            transform_type = 'affine';
        end
    end

    if strcmp(transform_type, 'affine')
        M = [1 + params(3), params(4), params(1); ...
          params(5), 1 + params(6), params(2)];
    elseif strcmp(transform_type, 'rigid')
        theta = params(3);
        M = [cos(theta), -sin(theta), params(1); ...
             sin(theta), cos(theta), params(2)];
    elseif strcmp(transform_type, 'rigid_scale')
        theta = params(3);
        scale = 1 + params(4);
        M = [cos(theta) * scale, -sin(theta) * scale, params(1); ...
             sin(theta) * scale, cos(theta) * scale, params(2)];
    elseif strcmp(transform_type, 'translate')
        M = [1, 0, params(1); ...
             0, 1, params(2)];
    end
end


function a = i4mat_flip_rows ( m, n, a )

%*****************************************************************************80
%
%% I4MAT_FLIP_ROWS swaps the rows of an I4MAT.
%
%  Discussion:
%
%    An I4MAT is an integer matrix.
%
%    To "flip" the rows of an IMAT is to start with something like
%
%      11 12 13 14 15
%      21 22 23 24 25
%      31 32 33 34 35
%      41 42 43 44 45
%      51 52 53 54 55
%
%    and return
%
%      51 52 53 54 55
%      41 42 43 44 45
%      31 32 33 34 35
%      21 22 23 24 25
%      11 12 13 14 15
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    22 June 2006
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer M, N, the number of rows and columns.
%
%    Input, integer A[M,N], the matrix whose rows are to be flipped.
%
%    Output, integer A[M,N], the flipped matrix.
%
  for i = 1 : floor ( m / 2 )
    b(1,    1:n) = a(    i,1:n);
    a(    i,1:n) = a(m+1-i,1:n);
    a(m+1-i,1:n) = b(1,    1:n);
  end

  return
end


function [sos,g] = driving_function_imp_nfchoa_pw(N,R,conf)
%DRIVING_FUNCTION_IMP_NFCHOA_PW second-order section representation for a
%plane wave in NFC-HOA
%
%   Usage: sos = driving_function_imp_nfchoa_pw(N,R,conf)
%
%   Input parameters:
%       N       - order of spherical Hankel function
%       R       - radius of secondary source array / m
%       conf    - configuration struct (see SFS_config)
%
%   Output parameters:
%       sos     - second-order section representation
%       g       - scalar gain factor
%
%   See also: sound_field_imp, sound_field_imp_nfchoa,
%       driving_function_imp_nfchoa
%
%   References:
%       Spors, Kuscher, Ahrens (2011) - "Efficient realization of model-based
%       rendering for 2.5-dimensional near-field compensated higher order
%       Ambisonics", IEEE Workshop on Applications of Signal Processing to Audio
%       and Acoustics (WASPAA), pp. 61-64,
%       https://doi.org/10.1109/ASPAA.2011.6082325

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

%% ===== Checking of input  parameters ==================================
nargmin = 3;
nargmax = 3;
narginchk(nargmin,nargmax);
isargpositivescalar(N,R);
isargstruct(conf);

%% ===== Configuration ==================================================
c = conf.c;
dimension = conf.dimension;
driving_functions = conf.driving_functions;

%% ===== Computation =====================================================
% Find spherical Hankel function zeros
[z,p] = sphbesselh_zeros(N);

% Get the delay and weighting factors
if strcmp('2D',dimension)

    % === 2-Dimensional ==================================================

    switch driving_functions
    case 'default'
        % --- SFS Toolbox ------------------------------------------------
        to_be_implemented;
    otherwise
        error(['%s: %s, this type of driving function is not implemented', ...
            'for a 2D plane wave.'],upper(mfilename),driving_functions);
    end

elseif strcmp('2.5D',dimension) || strcmp('3D',dimension)

    % === 2.5- & 3-Dimensional ==========================================

    switch driving_functions
    case 'default'
        % --- SFS Toolbox ------------------------------------------------
        % 2.5D for a plane wave as source model
        %
        [sos, g] = zp2sos(p,z*c/R,1,'down','none');
        g = g * (-1)^abs(N) * 4*pi * R;
        %
        % Compare Spors et al. (2011), eq. (10)
        %
    otherwise
        error(['%s: %s, this type of driving function is not implemented', ...
            'for a 2.5D plane wave.'],upper(mfilename),driving_functions);
    end

else
    error('%s: the dimension %s is unknown.',upper(mfilename),dimension);
end


function Y = acosh(X)
    %ACOSH  Symbolic inverse hyperbolic cosine.

    % Convert inputs to SymExpression
    % X = SymExpression(X);

    % construct the operation string
    sstr = ['ArcCosh[' X.s ']'];

    % create a new object with the evaluated string
    Y = SymExpression(sstr);
end


% This function compute the modulation spectrum of speech. The modulation
% spectrum can be derived from several types of feature trajectories: 1)
% log spectrogram; 2) log Mel filterbanks; 3) MFCC. Note that it is
% different from another definition of modulation spectrum that derived
% from the evelop of band-passed time domain signal.
% Inputs:
%   wav: the time domain signal
%   para: a structure that controls various options in the function
% Outputs:
%   modu: the modulation spectrum (complex valued) of feature blocks
%   moduAvg: the average modulation spectrum (magnitude) of the input signal
%
% Author: Xiao Xiong
% Created: 1 Dec 2014
% Last Modified: 5 Dec 2014
%
function [modu, moduAvg] = compute_modulation_stft(wav, para)
type = para.type;
fs = para.fs;
win_size = para.win_size;
win_shift = para.win_shift;

spec = wav2abs(wav, fs);

if type == 1    % compute from spectrum
    data = log(spec);
elseif type == 2    % compute from Mel filterbanks
    data = log(abs2Mel(spec, fs));
elseif type == 3    % compute from static MFCC
    data = fbank2mfcc(log(abs2Mel(spec, fs)));
end

% Perform optional normalization to the data
if isfield(para, 'norm')
    switch para.norm
        case 'CMN'
            data = CMN(data);
        case 'MVN'
            data = MVN(data);
        otherwise
            fprintf('Unknown normalization %s\n', para.norm);
    end
end

[nFr, dim] = size(data);

nBlockMax = ceil( (nFr-win_size)/win_shift ) + 1;
modu = []; moduAvg = [];
nBlock = 0;
for i=1:win_shift:nFr
    j = min(nFr, i+win_size-1);
    if j-i+1 < win_size/2   % if the remaining frames is not enough for half of the window size, discard it.
        break;
    end
    nBlock = nBlock + 1;
    modu(:,:,nBlock) = fft(data(i:j,:), win_size);
    last_block_size = (j-i)+1;
end

if size(modu,3)==0
    return;
end

modu = modu(1:win_size/2+1,:,:);

moduAvg = sum(abs(modu(:,:,1:end-1)),3) + abs(modu(:,:,end)) * last_block_size/win_size;
moduAvg = moduAvg / (size(modu,3)-1+last_block_size/win_size);

end


function [xUpdate,PUpdate,innov,Pzz,W]=cubKalUpdate(xPred,PPred,z,R,h,xi,w,innovTrans,measAvgFun,stateDiffTrans,stateTrans)
%CUBKALUPDATE Perform the measurement update step in the cubature Kalman
%             filter with additive measurement noise. Unlike the square
%             root version, one can use cubature points with negative
%             weights.
%
%INPUTS: xPred The xDim X 1 predicted target state.
%        PPred The xDim X xDim predicted state covariance matrix.
%            z The zDim X 1 vector measurement.
%            R The zDim X zDim measurement covariance matrix in the native
%              coordinate system of the measurement.
%            h A function handle for the measurement function that takes
%              the state as its argument.
%           xi An xDim X numCubPoints matrix of cubature points. If this
%              and the next parameter are omitted or empty matrices are
%              passed, then fifthOrderCubPoints(xDim) is used. It is
%              suggested that xi and w be provided to avoid needless
%              recomputation of the cubature points.
%            w A numCubPoints X 1 vector of the weights associated with the
%              cubature points.
%   innovTrans An optional function handle that computes and optionally
%              transforms the value of the difference between the
%              observation and any predicted points. This is called as
%              innovTrans(a,b) and the default if omitted or an empty
%              matrix is passed is @(a,b)bsxfun(@minus,a,b). This must be
%              able to handle sets of values. For a zDimX1 measurement,
%              either of the inputs could be zDimXN in size while one of
%              the inputs could be zDimX1 in size.  This only needs to be
%              supplied when a measurement difference must be restricted
%              to a certain range. For example, the innovation between two
%              angles will be 2*pi if one angle is zero and the other
%              2*pi, even though they are the same direction. In such an
%              instance, a function handle to the
%              wrapRange(bsxfun(@minus,a,b),-pi,pi) function with the
%              appropriate parameters should be passed for innovTrans.
%   measAvgFun An optional function handle that, when given N measurement
%              values with weights, produces the weighted average. This
%              function only has to be provided if the domain of the
%              measurement is not linear. For example, when averaging
%              angular values, then the function meanAng should be used.
% stateDiffTrans An optional function handle that takes an xDimXN matrix of
%              N differences between states and transforms them however
%              might be necessary. If not transformation is necessary, this
%              parameter can be omitted or an empty matrix passed.
%   stateTrans An optional function that takes a state estimate and
%              transforms it. This is useful if one wishes the elements of
%              the state to be bound to a certain domain. For example, if
%              an element of the state is an angle, one might generally
%              want to bind it to the region +/-pi.
%
%OUTPUTS: xUpdate The xDim X 1 updated state vector.
%         PUpdate The updated xDim X xDim state covariance matrix.
%      innov, Pzz The zDimX1 innovation and the zDimXzDim innovation
%                 covariance matrix are returned in case one wishes to
%                 analyze the consistency of the estimator or use those
%                 values in gating or likelihood evaluation.
%               W The xDimXzDim gain used in the update. This can be
%                 useful when gating and using the function
%                 calcMissedGateCov.
%
%If the function h needs additional parameters beyond the state, then the
%parameters can be passed by using an anonymous function as the function
%handle. For example, suppose that the measurement function is measFunc and
%it needs the additional parameters param1 and param2. In this instance,
%rather than using
%h=@measFunc
%one should use
%h=@(x)measFunc(x,param1,param2)
%This way, every time cubKalUpdate calls measFunc (via h) with a
%different x, those two parameters are always passed.
%
%The mathematics behind the function cubKalUpdate are described in
%detail in Section IX of [1] and in [2]. Note that this is essentially the
%"unscented Kalman filter" with additive noise. One simply has to provide
%the filter with the appropriate cubature points and weights.
%
%The optional parameters innovTrans and measAvgFun are not described in
%references [1] and [2], but allow for possible modifications to the filter
%as described in [3]. The parameters have been added to allow the filter to
%be used with angular quantities. For example, if the measurement consisted
%of range and angle, z=[r;theta], then
%innovTrans=@(a,b)[bsxfun(@minus,a(1,:),b(1,:));
%                  wrapRange(bsxfun(@minus,a(2,:),b(2,:)),-pi,pi)];
%measAvgFun=@(z,w)[calcMixtureMoments(z(1,:),w);
%                  meanAng(z(2,:),w')];
%should be used to approximately deal with the circular nature of the
%measurements.
%
%REFERENCES:
%[1] D. F. Crouse , "Basic tracking using nonlinear 3D monostatic and
%    bistatic measurements," IEEE Aerospace and Electronic Systems Magazine,
%    vol. 29, no. 8, Part II, pp. 4-53, Aug. 2014.
%[2] I. Arasaratnam and S. Haykin, "Cubature Kalman filters," IEEE
%    Transactions on Automatic Control, vol. 54, no. 6, pp. 1254-1269,
%    Jun. 2009.
%[3] D. F. Crouse, "Cubature/ unscented/ sigma point Kalman filtering with
%    angular measurement models," in Proceedings of the 18th International
%    Conference on Information Fusion, Washington, D.C., 6-9 Jul. 2015.
%
%September 2015 David F. Crouse, Naval Research Laboratory, Washington D.C.
%(UNCLASSIFIED) DISTRIBUTION STATEMENT A. Approved for public release.

    xDim=size(xPred,1);

    if(nargin<6||isempty(xi))
        [xi,w]=fifthOrderCubPoints(xDim);
    end

    if(nargin<8||isempty(innovTrans))
        %The function just returns the input difference.
        innovTrans=@(a,b)bsxfun(@minus,a,b);
    end

    if(nargin<9||isempty(measAvgFun))
        measAvgFun=@(zPoints,w)calcMixtureMoments(zPoints,w);
    end

    if(nargin<10||isempty(stateDiffTrans))
        stateDiffTrans=@(x)x;
    end

    if(nargin<11||isempty(stateTrans))
        stateTrans=@(x)x;
    end

    zDim=size(z,1);

    numCubPoints=size(xi,2);

    %cholSemiDef is used instead of chol in case a positive semi-definite
    %covariance matrix is passed.
    SPred=cholSemiDef(PPred,'lower');
    %Predicted cubature state points
    xPredPoints=stateTrans(transformCubPoints(xi,xPred,SPred));

    %Predicted cubature measurement points
    zPredPoints=zeros(zDim,numCubPoints);
    for curP=1:numCubPoints
        zPredPoints(:,curP)=h(xPredPoints(:,curP));
    end

    %Measurement prediction.
    zPred=measAvgFun(zPredPoints,w);

    %The innovation, transformed as necessary to keep values in a desired
    %range.
    innov=innovTrans(z,zPred);

    xPredCenPoints=stateDiffTrans(bsxfun(@minus,xPredPoints,xPred));
    %Centered, predicted cubature measurement points, transformed as
    %necessary to keep the values within a desired range.
    zPredCenPoints=innovTrans(zPredPoints,zPred);
    Pzz=R;
    Pxz=zeros(xDim,zDim);
    for curP=1:numCubPoints
        diff=zPredCenPoints(:,curP);
        Pzz=Pzz+w(curP)*(diff*diff');
        Pxz=Pxz+w(curP)*xPredCenPoints(:,curP)*diff';
    end

    %The filter gain
    W=Pxz/Pzz;

    %Updated state estimate
    xUpdate=stateTrans(xPred+W*innov);

    %Updated state covariance matrix
    %We could just do a simple one-line solution as in [1] and [2].
    %However, that does not guarantee that PUpdate will always be position
    %(semi)definite. Thus, we use an equivalent but more complicated update
    %formula based on Equation in Appendix C of [1].
    PUpdate=W*R*W';
    for curP=1:numCubPoints
        diff=stateDiffTrans(xPredCenPoints(:,curP)-W*zPredCenPoints(:,curP));
        PUpdate=PUpdate+w(curP)*(diff*diff');
    end
    %Ensure symmetry
    PUpdate=(PUpdate+PUpdate')/2;
end

%LICENSE:
%
%The source code is in the public domain and not licensed or under
%copyright. The information and software may be used freely by the public.
%As required by 17 U.S.C. 403, third parties producing copyrighted works
%consisting predominantly of the material produced by U.S. government
%agencies must provide notice with such work(s) identifying the U.S.
%Government material incorporated and stating that such material is not
%subject to copyright protection.
%
%Derived works shall not identify themselves in a manner that implies an
%endorsement by or an affiliation with the Naval Research Laboratory.
%
%RECIPIENT BEARS ALL RISK RELATING TO QUALITY AND PERFORMANCE OF THE
%SOFTWARE AND ANY RELATED MATERIALS, AND AGREES TO INDEMNIFY THE NAVAL
%RESEARCH LABORATORY FOR ALL THIRD-PARTY CLAIMS RESULTING FROM THE ACTIONS
%OF RECIPIENT IN THE USE OF THE SOFTWARE.


% determine searching area
active_pixel = (sum(neuron_bk.A(:,IDs), 2)>0);

% update spatial/temporal components of the merged neuron
data = neuron_bk.A(active_pixel, IDs)*neuron_bk.C_raw(IDs, :);
ci = neuron_bk.C_raw(IDs(1), :);
for miter=1:10
    ai = data*ci'/(ci*ci');
    ci = ai'*data/(ai'*ai);
end

sn = GetSn(ci);
neuron.A(active_pixel, IDs(1)) = ai*sn;
neuron.C_raw(IDs(1), :) = ci/sn;
neuron.S(IDs(1), :) = ci/sn;
newIDs(IDs(1)) = IDs(1);
% remove merged elements
ind_del(IDs(2:end)) = true;

%% show merged shape and trace
axes(ax_merged);
neuron.image(neuron.A(:, IDs(1)));
axis equal off tight;
set(gca, 'xlim', get(ax_selected, 'xlim'));
set(gca, 'ylim', get(ax_selected, 'ylim'));

axes(ax_merged_trace);
plot(ci, 'k', 'linewidth', 2);

%% clear IDs;
IDs_last = IDs;
IDs = [];


function grad = B_MVDR_spatialCov(X, curr_layer, beamform_layer, after_power_layer)

% X is the multichannel complex spectrum inputs
[D,C,T,N] = size(X);
% weight is the beamforming weight
weight = reshape(curr_layer.a, D,C,N);
lambda = curr_layer.lambda;
phi_s = curr_layer.phi_s;
phi_n = curr_layer.phi_n;

if isfield(curr_layer, 'noiseCovL2')
    noiseCovL2 = curr_layer.noiseCovL2;
else
    noiseCovL2 = 0;  % add noiseCovRegularization*\lambda*I to noise covariance, where \lambda is the maximum eigenvalue
end

% Y is the beamforming's output
Y = beamform_layer.a;
% future_grad is the gradient of the power specturm of beamformed signal
future_grad = after_power_layer.grad;

if N>1
    [validMask, variableLength] = getValidFrameMask(curr_layer);
    future_gradUtt = ExtractVariableLengthTrajectory(future_grad, validMask);
    YUtt = ExtractVariableLengthTrajectory(Y, validMask);
    X = reshape(X, D*C, T, N);
    XUtt = ExtractVariableLengthTrajectory(X, validMask);
    for i=1:N
        grad{i} = GetGradUtt(reshape(XUtt{i},D,C,size(XUtt{i},2)),YUtt{i},phi_s(:,:,:,:,i), phi_n(:,:,:,:,i), ...
            weight(:,:,i), lambda(:,:,:,:,i), future_gradUtt{i}, noiseCovL2);
    end
    grad = cell2mat_gpu(grad);
    grad = permute(grad, [1 3 2]);
else
    grad = GetGradUtt(X,Y,phi_s, phi_n, weight, lambda, future_grad, noiseCovL2);
end

end

%%
function grad = GetGradUtt(X,Y,phi_s, phi_n, weight, lambda, future_grad, noiseCovL2)
[D,C,T,N] = size(X);
u = zeros(C,1);
u(1) = 1;
for f=1:D
    x = squeeze(X(f,:,:));
    xx = x*x';
    ww = weight(f,:).' * conj(weight(f,:));
    phi_n_inv = inv(phi_n{1,1,f});
    yy = abs(Y(f,:).*conj(Y(f,:)));
    dyyy = sum(future_grad(f,:).*yy);
    dyxx = bsxfun(@times, x, future_grad(f,:)) * x';

    if 1
        grad_phi_s(:,:,f) = 2 * phi_n_inv * dyxx * weight(f,:).' * u' / lambda{1,1,f} -2 * dyyy * phi_n_inv / lambda{1,1,f};
        grad_phi_n(:,:,f) = - 2* phi_n_inv * dyxx * ww + 2 * dyyy * phi_n_inv * phi_s{1,1,f} * phi_n_inv / lambda{1,1,f};
    elseif 0    % no lambda
        grad_phi_s(:,:,f) = 2 * phi_n_inv * dyxx * weight(f,:).' * u';
        grad_phi_n(:,:,f) = - 2* phi_n_inv * dyxx * ww;
    else    % only lambda
        grad_phi_s(:,:,f) = -2 * dyyy * phi_n_inv / lambda{1,1,f};
        grad_phi_n(:,:,f) = 2 * dyyy * phi_n_inv * phi_s{1,1,f} * phi_n_inv / lambda{1,1,f};
    end
end

grad = conj([reshape(grad_phi_s,C*C*D,1); reshape(grad_phi_n,C*C*D,1)]);
end


%% load
load cnt_ssvep;
CNT = cnt_ssvep;

% cnt variables
% cnt.t  : time information
% cnt.fs : sampling frequency
% cnt.y_dec : class information (e.g., up = 1, left = 2, right = 3, down = 4)
% cnt.y_logic : logical format of class inforamtion
% cnt_y_class : class name (e.g., up, left, right, and down)
% cnt.class : number of class
% cnt.chan : number of electrodes
% cnt. x : raw eeg signals

% revised 2017.11.11 - Oyeon Kwon (oy_kwon@korea.ac.kr)
%% Initialization
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
time = 4;
freq = [5, 7, 9, 11];
fs = CNT.fs;
interval = [0 4000];  %
marker  = {'1','up';'2', 'left';'3', 'right';'4', 'down'};
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% CCA - Analysis
SMT = prep_segmentation(CNT, {'interval', interval});

numTrials = size(SMT.x, 2);
count= numTrials;

for i = 1: size(SMT.x, 2)
    cca_result = ssvep_cca_analysis(squeeze(SMT.x(:,i,:)),{'marker',marker;'freq', freq;'fs', fs;'time',time});
    [~, ind] = max(cca_result);
    if SMT.y_dec(i) ~= ind
        count = count -1;
    end
end
fprintf('Accuracy: %.2f%%\n', count/numTrials);
Accuracy = count/numTrials;


classdef IncrementalSequence < handle

    properties (GetAccess = public, SetAccess = protected)
        value
    end

    properties (Access = public)
        alpha
        initialValue
        finalValue
    end

    properties (Access = protected)
        x0
        x1
        nSteps
    end

    methods (Access = protected, Abstract)

        generateAlphaSequence(obj)

    end

    methods (Access = public)

        function update(obj,i)
            obj.value = (1-obj.alpha(i))*obj.initialValue + obj.alpha(i)*obj.finalValue;
        end

    end

    methods (Access = protected)

        function init(obj,x0,x1,nSteps,a0,a1)
            obj.x0 = x0;
            obj.x1 = x1;
            obj.nSteps = nSteps;
            obj.initialValue = a0;
            obj.finalValue = a1;
        end

    end

end


function [N] = lbf2N(lbf)
% Convert force from pounds to newtons.
% Chad A. Greene 2012
N = lbf*4.4482216152605;


seed = 107;
rng(seed);
L = 20;
N = 6;
T = 3000;
W = zeros(N,1,L);
for ni = 0:N-1
    W(ni+1,1,(mod(1:L,N+1)-1)==ni)=1;
end
H = (rand(1,T)<.005);
H(3*L) = 1;
imagesc(squeeze(W));shg
SimpleWHPlot(W,H);
%%
set(gca, 'xscale', 'log', 'ytick', [], 'color', 'none')
set(gca,'color','none','tickdir','out','ticklength', [0.025, 0.025])
savedir = 'C:\Users\emackev\Dropbox (MIT)\SeqNMF\Figures';
saveTitle = 'L1Example';
papersize = [4 2];

[W,H] = seqNMF(helper.reconstruct(W,H)+.0*rand(N,T), 'K', 3, 'L', 2*L,...
    'lambdaL1H', 10, 'lambda', .001, 'maxiter', 150);
SimpleWHPlot(W,H,[],0)
set(gcf, 'papersize', papersize, 'paperposition', [0 0 papersize]);%, 'color', 'none')
saveas(gcf, fullfile(savedir, [saveTitle '_L1H1.pdf']));

[W,H] = seqNMF(helper.reconstruct(W,H)+.0*rand(N,T), 'K', 3, 'L', 2*L,...
    'lambdaL1W', 100, 'lambda', .001, 'maxiter', 150);
SimpleWHPlot(W,H,[],0)
set(gcf, 'papersize', papersize, 'paperposition', [0 0 papersize]);%, 'color', 'none')
saveas(gcf, fullfile(savedir, [saveTitle '_L1W1.pdf']));