function test_bug2994

% MEM 2gb
% WALLTIME 00:10:00
% DEPENDENCY ft_freqstatistics ft_freqgrandaverage ft_selectdata

% Bug 2994 has been reported by Niels. Apparently once upon a time it was
% possible to call ft_freqstatistics with ft_freqgrandaveraged data in the
% input, where the dimord is 'subj_freq_time' (NOTE the lacking chan
% dimord) with the data containing apparently an average across channels.
% The input data contains a label field. ft_freqstatistics crashes when the
% low level function tries to cluster across space.

% Let's start this form the beginning, create a multisubject
% freq-structure, with data averaged across channels, and see whether it
% pulls through

tmp.freq   = [1 2 3];
tmp.time   = [1 2 3 4 5];
tmp.label  = {'a';'b'};
tmp.dimord = 'chan_freq_time';

freq = cell(1,10);
freq0 = cell(1,10);
for k = 1:10
  tmp.powspctrm = rand(2,3,5);
  freq{k} = tmp;
  tmp.powspctrm(:) = 0;
  freq0{k} = tmp;
end

%% ------------------------------------------------------------------------
% here, the following is done:
% -ft_freqgrandaverage (keepindividual = 'yes')
% -ft_selectdata (avgoverchan = 'yes')
% -ft_freqstatistics

cfg1 = [];
cfg1.keepindividual = 'yes';

cfg2 = [];
cfg2.avgoverchan = 'yes';

cfg3 = [];
cfg3.method    = 'montecarlo';
cfg3.statistic = 'ft_statfun_depsamplesT';
cfg3.design    = [ones(1,10) ones(1,10)*2;1:10 1:10];
cfg3.numrandomization = 100;
cfg3.correctm  = 'cluster';
cfg3.ivar      = 1;
cfg3.uvar      = 2;

freqall  = ft_selectdata(cfg2, ft_freqgrandaverage(cfg1, freq{:}));
freqall0 = ft_selectdata(cfg2, ft_freqgrandaverage(cfg1, freq0{:}));
assert(isequal(size(freqall.powspctrm),[10 1 3 5])); % there should be a singleton channel dimension
stat1    = ft_freqstatistics(cfg3, freqall, freqall0);

%% ------------------------------------------------------------------------
% here, the following is done:
% -ft_freqgrandaverage (keepindividual = 'yes')
% -ft_freqstatistics (avgoverchan = 'yes')

cfg1 = [];
cfg1.keepindividual = 'yes';

cfg3 = [];
cfg3.method    = 'montecarlo';
cfg3.statistic = 'ft_statfun_depsamplesT';
cfg3.avgoverchan = 'yes';
cfg3.design    = [ones(1,10) ones(1,10)*2;1:10 1:10];
cfg3.numrandomization = 100;
cfg3.correctm  = 'cluster';
cfg3.ivar      = 1;
cfg3.uvar      = 2;

freqall  = ft_freqgrandaverage(cfg1, freq{:});
freqall0 = ft_freqgrandaverage(cfg1, freq0{:});
assert(isequal(size(freqall.powspctrm),[10 2 3 5])); % there should be two channels
stat2    = ft_freqstatistics(cfg3, freqall, freqall0);

%% ------------------------------------------------------------------------
% here, the following is done:
% - NO ft_freqgrandaverage
% -ft_freqstatistics (avgoverchan = 'yes')

cfg3 = [];
cfg3.method    = 'montecarlo';
cfg3.statistic = 'ft_statfun_depsamplesT';
cfg3.avgoverchan = 'yes';
cfg3.design    = [ones(1,10) ones(1,10)*2;1:10 1:10];
cfg3.numrandomization = 100;
cfg3.correctm  = 'cluster';
cfg3.ivar      = 1;
cfg3.uvar      = 2;
cfg3.avgoverchan = 'yes';

stat3    = ft_freqstatistics(cfg3, freq{:}, freq0{:});

%% ------------------------------------------------------------------------
% here, the following is done:
% -ft_appendfreq
% -ft_freqstatistics (avgoverchan = 'yes')

cfg1 = [];
cfg1.parameter = 'powspctrm';
cfg1.appenddim = 'rpt';

cfg3 = [];
cfg3.method    = 'montecarlo';
cfg3.statistic = 'ft_statfun_depsamplesT';
cfg3.avgoverchan = 'yes';
cfg3.design    = [ones(1,10) ones(1,10)*2;1:10 1:10];
cfg3.numrandomization = 100;
cfg3.correctm  = 'cluster';
cfg3.ivar      = 1;
cfg3.uvar      = 2;
cfg3.avgoverchan = 'yes';

freqall  = ft_appendfreq(cfg1, freq{:});
freqall0 = ft_appendfreq(cfg1, freq0{:});

stat4    = ft_freqstatistics(cfg3, freqall, freqall0);

assert(isequal(stat1.stat,stat2.stat));
assert(isequal(stat1.stat,stat3.stat));
assert(isequal(stat1.stat,stat4.stat));
assert(isequal(stat2.stat,stat3.stat));
assert(isequal(stat2.stat,stat4.stat));
assert(isequal(stat3.stat,stat4.stat));

% the conclusion so far is that with the current state of the code, all
% seems to work fine.


function [result]=randSimpleMatrix(size)
    result=rand(size,'double', 'gpuArray');
end


function varargout = mvncdf(varargin)

switch class(varargin{1})

    case {'sdpvar','ndsdpvar'}

        if nargin > 1
            error('sdpvar/mvncdf currently only supports 1 argument, i.e. assumed zero mean and unit variance');
        end
        varargin{1} = reshape(varargin{1},[],1);
        varargout{1} = yalmip('define',mfilename,varargin{1});

    case 'char'

        X = varargin{3};
        F = [];

        operator = CreateBasicOperator('positive','increasing','callback');
        if length(X) == 1
            operator.convexity = @convexity;
            operator.derivative = @(x)(1/2)*(1/sqrt(2))*exp(-(x/sqrt(2)).^2)*2/sqrt(pi);
            operator.inverse = @(x)(sqrt(2)*erfinv(2*x-1));
        else
            operator.bounds = @bounds;
        end

        operator.range = [0 1];
        varargout{1} = F;
        varargout{2} = operator;
        varargout{3} = X;

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

function [L,U] = bounds(xL,xU)
L = mvncdf(xL);
U = mvncdf(xU);

function vexity = convexity(xL,xU)
if xL >= 0
    vexity = 'concave';
elseif xU <= 0
    vexity = 'convex';
else
    vexity = 'none';
end


function [colormap]=cbrewer(cname, ncol, interp_method)
% CBREWER - This function produces a colorbrewer table (rgb data) for a
% given type, name and number of colors of the colorbrewer tables.
% For more information on 'colorbrewer', please visit
% http://colorbrewer2.org/
%
% [colormap]=cbrewer(cname, ncol, interp_method)
%
% INPUT:
%  cname  name of colortable. One of the following
%      Sequential tables:
%        'Blues'
%        'BuGn'
%        'BuPu'
%        'GnBu'
%        'Greens'
%        'Greys'
%        'Oranges'
%        'OrRd'
%        'PuBu'
%        'PuBuGn'
%        'PuRd'
%        'Purples'
%        'RdPu'
%        'Reds'
%        'YlGn'
%        'YlGnBu'
%        'YlOrBr'
%        'YlOrRd'
%      Divergent tables:
%        'BrBG'
%        'PiYG'
%        'PRGn'
%        'PuOr'
%        'RdBu'
%        'RdGy'
%        'RdYlBu'
%        'RdYlGn'};
%      Qualitative tables:
%        'Accent'
%        'Dark2'
%        'Paired'
%        'Pastel1'
%        'Pastel2'
%        'Set1'
%        'Set2'
%        'Set3'
%   ncol  number of color in the table.
%   interp_method  if the table need to be interpolated, what method
%                  should be used for interp1? Default='pchip' (aka 'cubic')
%
% A note on the number of colors: Based on the original data, there is
% only a certain number of colors available for each type and name of
% colortable. When 'ncol' is larger then the maximum number of colors
% originally given, an interpolation routine is called (interp1) to produce
% the "extended" colormaps.
%
% See also: plot_brewer_cmap
%

%
% The tables were generated from an MS-Excel file provided on the website
% http://www.personal.psu.edu/cab38/ColorBrewer/ColorBrewer_updates.html
%
% This product includes color specifications and designs developed by
% Cynthia Brewer (http://colorbrewer.org/).
%
%
% Author: Charles Robert
% email: tannoudji@hotmail.com
% Date: 06.12.2011

% load colorbrewer data
load('colorbrewer.mat')
% initialise the colormap is there are any problems
colormap=[];
if (~exist('interp_method', 'var'))
    interp_method='pchip';
end

ctype_names={'div', 'seq', 'qual'};
ctype = [];
for try_ctype = ctype_names
  try_ctype = try_ctype{1};
  if isfield(colorbrewer.(try_ctype),cname)
    ctype = try_ctype;
    break;
  end
end
if isempty(ctype)
  error(['Could not find colormap named: ' cname]);
end

if (ncol>length(colorbrewer.(ctype).(cname)))
  cbrew_init=colorbrewer.(ctype).(cname){length(colorbrewer.(ctype).(cname))};
  colormap=interpolate_cbrewer(cbrew_init, interp_method, ncol);
  colormap=colormap./255;
  return
end

if isempty(colorbrewer.(ctype).(cname){ncol})

  while isempty(colorbrewer.(ctype).(cname){ncol})
    ncol=ncol+1;
  end
  warning( ...
    ['The minimum number of colors for table ' cname ...
     ' is ' num2str(ncol) '.'])
end

colormap=(colorbrewer.(ctype).(cname){ncol})./255;

end
% Copyright (c) 2011, Charles Robert
% All rights reserved.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are
% met:
%
%     * Redistributions of source code must retain the above copyright
%       notice, this list of conditions and the following disclaimer.
%     * Redistributions in binary form must reproduce the above copyright
%       notice, this list of conditions and the following disclaimer in
%       the documentation and/or other materials provided with the distribution
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
% POSSIBILITY OF SUCH DAMAGE.
%


%{
 * Copyright (C) 2020-2030, The Regents of The University of Michigan.
 * All rights reserved.
 * This software was developed in the Biped Lab (https://www.biped.solutions/)
 * under the direction of Jessy Grizzle, grizzle@umich.edu. This software may
 * be available under alternative licensing terms; contact the address above.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * The views and conclusions contained in the software and documentation are those
 * of the authors and should not be interpreted as representing official policies,
 * either expressed or implied, of the Regents of The University of Michigan.
 *
 * AUTHOR: Bruce JK Huang (bjhuang[at]umich.edu)
 * WEBSITE: https://www.brucerobot.com/
%}

function cost = getCost(x)
    % cost = exp(x);
    cost = x^2;
    cost = x;
end


function c=cuttype(t,j)
%CUTTYPE Type of cut used at branches in decision tree.
%   C=CUTTYPE(T) returns an N-element cell array C indicating the type of
%   cut at each node in the tree T, where N is the number of nodes in the
%   tree.  For each node K, C{K} is 'continuous' if the cut is defined in
%   the form Z<V for a variable Z and cutpoint V, 'categorical' if the cut
%   is defined by whether Z takes a value in a set of categories, or '' if
%   K is not a branch node.  The CUTVAR method returns the cutpoints for
%   'continuous' cuts, and the CUTCATEGORIES method returns the set of
%   categories for 'categorical' cuts.
%
%   V=CUTTYPE(T,J) takes an array J of node numbers and returns the cut
%   types for the specified nodes.
%
%   See also CLASSREGTREE, CLASSREGTREE/NUMNODES, CLASSREGTREE/CUTVAR.

%   Copyright 2006-2007 The MathWorks, Inc.
%   $Revision: 1.1.6.2 $  $Date: 2007/02/15 21:48:06 $

if nargin>=2 && ~validatenodes(t,j)
    error('stats:classregtree:parent:InvalidNode',...
          'J must be an array of node numbers or a logical array of the proper size.');
end

% Get variable defining each cut
if nargin<2
    v = t.var;
else
    v = t.var(j,:);
end
v = v(:);

% Create array of cut types
c = repmat({''},numel(v),1);
c(v>0) = {'continuous'};
c(v<0) = {'categorical'};


function [fevd_record,fevd_estimates]=panel2fevd(struct_irf_record,gamma_record,It,Bu,IRFperiods,n,FEVDband)

% because fevd is obtained from IRFs, and because IRFs are common to all units, fevd can be run only once
[fevd_record]=bear.fevd(struct_irf_record,gamma_record,It,Bu,n,IRFperiods,FEVDband);
% compute posterior estimates
[fevd_estimates]=bear.fevdestimates(fevd_record,n,IRFperiods,FEVDband);


function Population = subNSGAII(Problem,Population,Operator,N)
% Sub-optimizer in LSMOF (NSGA-II)

%------------------------------- Copyright --------------------------------
% Copyright (c) 2023 BIMK Group. You are free to use the PlatEMO for
% research purposes. All publications which use this platform or any code
% in the platform should acknowledge the use of "PlatEMO" and reference "Ye
% Tian, Ran Cheng, Xingyi Zhang, and Yaochu Jin, PlatEMO: A MATLAB platform
% for evolutionary multi-objective optimization [educational forum], IEEE
% Computational Intelligence Magazine, 2017, 12(4): 73-87".
%--------------------------------------------------------------------------

% This function is written by Cheng He

    FrontNo  = NDSort(Population.objs,inf);
    CrowdDis = CrowdingDistance(Population.objs,FrontNo);
    if Operator == 1
        MatingPool = TournamentSelection(2,N,FrontNo,-CrowdDis);
        Offspring  = OperatorGA(Problem,Population(MatingPool));
    else
        MatingPool1 = TournamentSelection(2,N,FrontNo,-CrowdDis);
        MatingPool2 = TournamentSelection(2,N,FrontNo,-CrowdDis);
        MatingPool3 = TournamentSelection(2,N,FrontNo,-CrowdDis);
        Offspring   = OperatorDE(Problem,Population(MatingPool1),Population(MatingPool2),Population(MatingPool3));
    end
    Population = EnvironmentalSelection([Population,Offspring],N);
end


%% Copyright (C) 2014-2017, 2019, 2022 Colin B. Macdonald
%%
%% This file is part of OctSymPy.
%%
%% OctSymPy 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 software 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 software; see the file COPYING.
%% If not, see <http://www.gnu.org/licenses/>.

%% -*- texinfo -*-
%% @documentencoding UTF-8
%% @deftypemethod  @@sym {@var{c} =} coeffs (@var{p}, @var{x})
%% @deftypemethodx @@sym {@var{c} =} coeffs (@var{p})
%% @deftypemethodx @@sym {@var{c} =} coeffs (@dots{}, 'all')
%% @deftypemethodx @@sym {[@var{c}, @var{t}] =} coeffs (@var{p}, @var{x})
%% @deftypemethodx @@sym {[@var{c}, @var{t}] =} coeffs (@var{p})
%% @deftypemethodx @@sym {[@var{c}, @var{t}] =} coeffs (@dots{}, 'all')
%% Return non-zero (or all) coefficients of symbolic polynomial.
%%
%% @var{c} contains the coefficients and @var{t} the corresponding
%% terms.
%%
%% Example:
%% @example
%% @group
%% syms x
%% [c, t] = coeffs (x^6 + 3*x - 4)
%%   @result{} c = (sym) [1  3  -4]  (1×3 matrix)
%%   @result{} t = (sym 1×3 matrix)
%%        ⎡ 6      ⎤
%%        ⎣x   x  1⎦
%% @end group
%% @end example
%%
%% The polynomial can be multivariate:
%% @example
%% @group
%% syms x y
%% [c, t] = coeffs (x^2 + y*x)
%%   @result{} c = (sym) [1  1]  (1×2 matrix)
%%   @result{} t = (sym 1×2 matrix)
%%       ⎡ 2     ⎤
%%       ⎣x   x⋅y⎦
%% @end group
%%
%% @group
%% [c, t] = coeffs (x^2 + y*x, [x y])   % same
%%   @result{} c = (sym) [1  1]  (1×2 matrix)
%%   @result{} t = (sym 1×2 matrix)
%%       ⎡ 2     ⎤
%%       ⎣x   x⋅y⎦
%%
%% [c, t] = coeffs (x^2 + y*x, @{x y@})   % same
%%   @result{} c = (sym) [1  1]  (1×2 matrix)
%%   @result{} t = (sym 1×2 matrix)
%%       ⎡ 2     ⎤
%%       ⎣x   x⋅y⎦
%% @end group
%% @end example
%%
%% You can use the second argument to specify a vector or list of
%% variables:
%% @example
%% @group
%% [c, t] = coeffs (x^2 + y*x, x)
%%   @result{} c = (sym) [1  y]  (1×2 matrix)
%%   @result{} t = (sym 1×2 matrix)
%%       ⎡ 2   ⎤
%%       ⎣x   x⎦
%% @end group
%% @end example
%%
%% Omitting the second output is not recommended, especially for non-interactive
%% code, because it gives only the non-zero coefficients, and additionally
%% the output is in the ``wrong order'' compared to other polynomial-related
%% commands:
%% @example
%% @group
%% c = coeffs (x^6 + 3*x - 4)
%%   @result{} c = (sym) [-4  3  1]  (1×3 matrix)
%% @end group
%% @end example
%% @strong{Warning:} Again, note the order is reversed from the two-output
%% case; this is for compatibility with Matlab's Symbolic Math Toolbox.
%%
%% If the optional input keyword @qcode{'all'} is passed, the zero
%% coefficients are returned as well, and in the familiar order.
%% @example
%% @group
%% c = coeffs (x^6 + 3*x - 4, 'all')
%%   @result{} c = (sym) [1  0  0  0  0  3  -4]  (1×7 matrix)
%% @end group
%% @end example
%% @strong{Note:} The @qcode{'all'} feature does not yet work with
%% multivariate polynomials (https://github.com/cbm755/octsympy/issues/720).
%%
%% @seealso{@@sym/sym2poly}
%% @end deftypemethod

function [c, t] = coeffs(p, x, all)

  if (nargin == 1)
    x = [];
    all = false;
  elseif (nargin == 2)
    if (ischar (x))
      assert (strcmpi (x, 'all'), ...
              'coeffs: invalid 2nd input: if string, should be "all"')
      x = [];
      all = true;
    else
      all = false;
    end
  elseif (nargin == 3)
    assert (strcmpi (all, 'all'), ...
            'coeffs: invalid 3rd input: should be string "all"')
    all = true;
  elseif (nargin > 3)
    print_usage ();
  end

  assert (isscalar (p), 'coeffs: works for scalar input only')

  p = sym(p);

  if (isempty (x))
    x = symvar (p);
    if (isempty (x))
      x = sym('x');  % any symbol
    end
  end

  x = sym(x);

  cmd = { '(f, xx, all) = _ins'
          'if not xx.is_Matrix:'
          '    xx = sp.Matrix([xx])'
          'xx = list(xx)'
          'p = Poly.from_expr(f, *xx)'
          'if all:'
          '    terms = p.all_terms()'
          'else:'
          '    terms = p.terms()'
          'cc = [q[1] for q in terms]'
          'tt = [1]*len(terms)'
          'for i, x in enumerate(p.gens):'
          '    tt = [t*x**q[0][i] for (t, q) in zip(tt, terms)]'
          'return (Matrix([cc]), Matrix([tt]))' };

  [c, t] = pycall_sympy__ (cmd, p, x, all);

  %% SMT compat:
  % reverse the order if t is not output.
  if (nargout <= 1) && (all == false)
    c = fliplr(c);
  end

  % if nargout == 1, its simplier to use 'p.coeffs()'
end

%!error coeffs (sym(1), 2, 3, 4)
%!error <should be> coeffs (sym(1), 2, 'al')
%!error <should be> coeffs (sym(1), 'al')

%!test
%! % simple
%! syms x
%! [c, t] = coeffs(6*x*x + 27);
%! assert (isequal (c, [6 27]))
%! assert (isequal (t, [x*x 1]))

%!test
%! % specify a variable
%! syms x
%! [c, t] = coeffs(6*x*x + 27, x);
%! assert (isequal (c, [6 27]))
%! assert (isequal (t, [x*x 1]))

%!test
%! % specify another variable
%! syms x y
%! [c, t] = coeffs(6*x + 27, y);
%! assert (isequal (c, 6*x + 27))
%! assert (isequal (t, 1))

%!test
%! % weird SMT order
%! syms x
%! a1 = [27 6];
%! a2 = [6 27];
%! c = coeffs(6*x*x + 27);
%! assert (isequal (c, a1))
%! coeffs(6*x*x + 27);
%! assert (isequal (ans, a1))
%! [c, t] = coeffs(6*x*x + 27);
%! assert (isequal (c, a2))

%!test
%! % no weird order with "all"
%! syms x
%! c = coeffs(6*x*x + 27, 'all');
%! assert (isequal (c, [6 0 27]))

%!test
%! % "all"
%! syms x
%! [c, t] = coeffs(6*x*x + 27, 'all');
%! assert (isequal (c, [6 0 27]))
%! assert (isequal (t, [x^2 x 1]))

%!test
%! % "All"
%! syms x
%! [c, t] = coeffs(6*x, 'All');
%! assert (isequal (c, [6 0]))
%! assert (isequal (t, [x 1]))

%!test
%! % multivariable array
%! syms x y
%! [c, t] = coeffs(6*x*x + 27*y*x  + 36, [x y]);
%! a = [6  27  36];
%! s = [x^2  x*y  1];
%! assert (isequal (c, a))
%! assert (isequal (t, s))
%! % with list
%! [c, t] = coeffs(6*x*x + 27*y*x  + 36, {x y});
%! assert (isequal (c, a))
%! assert (isequal (t, s))

%!test
%! % other symbols treated as part of coeffs
%! syms x y
%! [c, t] = coeffs(6*x*x + 27*y*x  + 36, x);
%! a = [6  27*y  36];
%! s = [x^2  x  1];
%! assert (isequal (c, a))
%! assert (isequal (t, s))

%!error <multivariate polynomials not supported>
%! % TODO: multivariate all not working (https://github.com/cbm755/octsympy/issues/720)
%! syms x y
%! [c, t] = coeffs(6*x^2 + 7*y + 19, [x y], 'all');

%!test
%! % empty same as not specifying; maybe not SMT compatible:
%! % https://github.com/cbm755/octsympy/pull/708#discussion_r94292831
%! syms x y
%! [c, t] = coeffs(6*x*x + 27*y*x  + 36, {});
%! a = [6  27  36];
%! assert (isequal (c, a))
%! [c, t] = coeffs(6*x*x + 27*y*x  + 36);
%! assert (isequal (c, a))

%!test
%! % no input defaults to all symbols (not symvar to get x)
%! syms x y
%! [c, t] = coeffs(6*x*x + 27*y*x  + 36);
%! assert (isequal (c, [6 27 36]))

%!test
%! % non sym input
%! syms x
%! assert (isequal (coeffs(6, x), sym(6)))

%!test
%! % constant input without x
%! assert (isequal (coeffs(sym(6)), sym(6)))

%!test
%! % constant input without x
%! assert (isequal (coeffs (sym(6), {}), sym(6)))

%! % irrational coefficients
%! syms x
%! f = x^2 + sqrt(sym(2))*x;
%! [c1, t1] = coeffs (f);
%! [c2, t2] = coeffs (f, x);
%! assert (isequal (c1, c2))
%! assert (isequal (t1, t2))


function [ap_results05,ap_results07,ap_resultsCOCO,mean_recall_per_IoU] = print_detection_evaluation_metrics(...
    all_bbox_gt, abbox_dets, classes)
%
% This file is part of the code that implements the following paper:
% Title      : "LocNet: Improving Localization Accuracy for Object Detection"
% Authors    : Spyros Gidaris, Nikos Komodakis
% Institution: Universite Paris Est, Ecole des Ponts ParisTech
% ArXiv link : http://arxiv.org/abs/1511.07763
% code       : https://github.com/gidariss/LocNet
%
% AUTORIGHTS
% --------------------------------------------------------
% Copyright (c) 2016 Spyros Gidaris
%
% Title     : "LocNet: Improving Localization Accuracy for Object Detection"
% ArXiv link: http://arxiv.org/abs/1511.07763
% Licensed under The MIT License [see LICENSE for details]
% ---------------------------------------------------------

% evaluate the average precision for IoU >=0.5
ap_results05 = evaluate_average_precision_pascal(all_bbox_gt, abbox_dets, classes );
fprintf('AP with IoU >= 0.5\n');
printAPResults(classes, ap_results05);
% evaluate the average precision for IoU >=0.7
ap_results07 = evaluate_average_precision_pascal(all_bbox_gt, abbox_dets, classes, 'minoverlap', 0.7);
fprintf('AP with IoU >= 0.7\n');
printAPResults(classes, ap_results07);
% evaluate the coco style average precision
ap_resultsCOCO = evaluate_average_precision_pascal(all_bbox_gt, abbox_dets, classes, 'coco_style', true);
fprintf('AP with coco style\n');
printAPResults(classes, ap_resultsCOCO);

num_classes = length(classes);
all_bbox_gt_no_difficults = remove_difficult_ground_truth_bboxes(all_bbox_gt);
all_bbox_gt_per_class = get_per_class_ground_truth_bboxes(all_bbox_gt_no_difficults, num_classes);

ave_recall_per_class = zeros(num_classes, 1);
recall_per_class     = cell(1,num_classes);

% compute the recall per IoU threshold and the average recall of the set of
% detections for each class separately
for j = 1:num_classes
    [ave_recall_per_class(j), recall_per_class{j}, thresholds] = ...
        compute_ave_recall_of_bbox(abbox_dets{j}, all_bbox_gt_per_class{j});
    recall_per_class{j} = recall_per_class{j}(:);
    recall_per_class{j} = recall_per_class{j}(thresholds>=0.5);
end
thresholds = thresholds(thresholds>=0.5);
fprintf('Average Recall of detections :\n');
printAPResults(classes, ave_recall_per_class);
fprintf('Recall per IoU of detections (averaged over the classes):\n');
mean_recall_per_IoU = mean(cell2mat(recall_per_class),2); % average over the classes
thresholds_string = strsplit(num2str(thresholds));
assert(length(mean_recall_per_IoU) == length(thresholds_string));
printAPResults(thresholds_string, mean_recall_per_IoU);
end


function value = r8_in_01 ( x )

%*****************************************************************************80
%
%% R8_IN_01 is TRUE if the value is in the range [0,1].
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    06 October 2004
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, real X, the value.
%
%    Output, logical VALUE, is TRUE if 0 <= X <= 1.
%
  if ( x < 0.0 || 1.0 < x )
    value = 0;
  else
    value = 1;
  end

  return
end


function [S,t,f,R,Serr]=mtspecgramtrigpb(data,E,win,movingwin,params,fscorr)
% Multi-taper event triggered time-frequency spectrum - binned point process
%
% Usage:
%
% [S,t,f,R,Serr]=mtspecgramtrigpb(data,E,win,movingwin,params,fscorr)
% Input:
%       data        (single channel data vector) -- required
%       E           (event times) - required
%       win         (in the form [winl,winr] i.e window around each event
%                                                 required
%       movingwin         (in the form [window,winstep] i.e length of moving
%                                                 window and step size) -
%                                                 required
%                                                 Note that units for the windows have
%                                                 to be consistent with
%                                                 units of Fs
%       params: structure with fields tapers, pad, Fs, fpass, err, trialave
%       - optional
%           tapers : precalculated tapers from dpss or in the one of the following
%                    forms:
%                   (1) A numeric vector [TW K] where TW is the
%                       time-bandwidth product and K is the number of
%                       tapers to be used (less than or equal to
%                       2TW-1).
%                   (2) A numeric vector [W T p] where W is the
%                       bandwidth, T is the duration of the data and p
%                       is an integer such that 2TW-p tapers are used. In
%                       this form there is no default i.e. to specify
%                       the bandwidth, you have to specify T and p as
%                       well. Note that the units of W and T have to be
%                       consistent: if W is in Hz, T must be in seconds
%                       and vice versa. Note that these units must also
%                       be consistent with the units of params.Fs: W can
%                       be in Hz if and only if params.Fs is in Hz.
%                       The default is to use form 1 with TW=3 and K=5
%                   Note that T has to be equal to movingwin(1).
%
%	        pad		    (padding factor for the FFT) - optional (can take values -1,0,1,2...).
%                    -1 corresponds to no padding, 0 corresponds to padding
%                    to the next highest power of 2 etc.
%			      	 e.g. For N = 500, if PAD = -1, we do not pad; if PAD = 0, we pad the FFT
%			      	 to 512 points, if pad=1, we pad to 1024 points etc.
%			      	 Defaults to 0.
%           Fs   (sampling frequency) - optional. Default 1.
%           fpass    (frequency band to be used in the calculation in the form
%                                   [fmin fmax])- optional.
%                                   Default all frequencies between 0 and Fs/2
%           err  (error calculation [1 p] - Theoretical error bars; [2 p] - Jackknife error bars
%                                   [0 p] or 0 - no error bars) - optional. Default 0.
%           trialave (average over events when 1, don't average when 0) - optional. Default 0
%       fscorr   (finite size corrections, 0 (don't use finite size corrections) or
%                1 (use finite size corrections) - optional
%                (available only for spikes). Defaults 0.
% Output:
%       S       (triggered time-frequency spectrum in form time x frequency x events if segave=0;
%               or in the form time x frequency segave=1)
%       t       (times)
%       f       (frequencies)
%       R       (spike rate)
%       Serr    (error bars) - only for err(1)>=1

if nargin < 4; error('Need data, events and parameters for the windows'); end;
if nargin < 5; params=[]; end;

if length(params.tapers)==3 & movingwin(1)~=params.tapers(2);
    error('Duration of data in params.tapers is inconsistent with movingwin(1), modify params.tapers(2) to proceed')
end

[tapers,pad,Fs,fpass,err,trialave,params]=getparams(params);
clear tapers pad fpass err trialave
if nargin < 6 || isempty(fscorr); fscorr=0; end;
data=change_row_to_column(data);
data=createdatamatpb(data,E,Fs,win);
if nargout==5;[S,t,f,R,Serr]=mtspecgrampb(data,movingwin,params,fscorr);
else [S,t,f,R]=mtspecgrampb(data,movingwin,params,fscorr);end;


filename = 'BulkCaseSym';%'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 = 32;

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 = 100;

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


function pass = test_std3(pref)
% Test the chebfun3/max command.

if ( nargin < 1 )
    pref = chebfunpref;
end
tol = 1e3*pref.cheb3Prefs.chebfun3eps;

f = chebfun3(10); % (x,y,z)-std of any fixed value is zero.
exact = 0;
h = std3(f);
pass(1) = norm(h - exact) < tol;

end


function aux_depthcontour(params, hParentFigure)
% function aux_FMD(params, hParentFigure);
%-------------------------------------------
%
% Incoming variables:
% params        : all variables
% hParentFigure : Handle of the parent figure
%
% Thomas van Stiphout, thomas@sed.ethz.ch
% last update: 7.9.2005

report_this_filefun(mfilename('fullpath'));

% prepare Value that will be illustrated on the 3D surface defined above
 params.mPlotValues=ones(length(params.vUsedNodes),1)*nan;
 params.mPlotValues(params.vUsedNodes)=params.mValueGrid(:,1);
 vSel=~isnan(params.mValueGrid(:,1));
 vTmp=params.mValueGrid(:,1);
 vTmp(~vSel)=nan;
 params.mPlotValues(params.vUsedNodes)=vTmp;
 %       params.mPlotValues(params.mPlotValues == 0)=nan;
 params.mPlotValues=reshape(params.mPlotValues,length(params.vY),length(params.vX));
 mZ_=params.mZ;
 mZ_(isnan(params.mPlotValues))=nan;
 V_=-10:-20:-190;
 hold on;contour3(params.mX,params.mY,-mZ_,V_,'k--');
 V_=-0:-20:-200;
 hold on;contour3(params.mX,params.mY,-mZ_,V_,'k-');
 hold on;contour(params.mX,params.mY,-mZ_,'ShowText','on');
%  figure;surf(params.mX,params.mY,-mZ_,params.mPlotValues);
      view(3)


function [irf_record_allt]=tvbvarirf4(beta_gibbs,sigma_t_gibbs,It,Bu,IRFperiods,n,m,p,k,T,signresperiods,signrestable)

% now identify all the periods concerned with restrictions
% first expand the non-empty entries in signresperiods since they are only expressed in intervals: transform into list
% for instance, translate [1 4] into [1 2 3 4]; I don't think this can done without a loop
temp=cell2mat(signresperiods(~cellfun(@isempty,signresperiods)));
periods=[];
for ii=1:size(temp,1)
    periods=[periods temp(ii,1):temp(ii,2)];
end
% suppress duplicates and sort
periods=sort(unique(periods))';
% count the total number of restriction periods (required for IRF matrix)
nperiods=size(periods,1);

% Identify the restriction matrices
% create five cells, corresponding to the three possible restrictions:
% one cell for sign restrictions, three cells for magnitude restrictions, one cell for zero restrictions
Scell=cell(1,n);
Mcell=cell(1,n);
Mlcell=cell(1,n);
Mucell=cell(1,n);
Zcell=cell(1,n);

% loop over rows and columns of the period matrix
for ii=1:n
    for jj=1:n
        % if entry (ii,jj) of the period matrix is not empty...
        if ~isempty(signresperiods{ii,jj})
            % ... then there is a restriction over one (or several) periods
            % loop overt those periods
            for kk=signresperiods{ii,jj}(1,1):signresperiods{ii,jj}(1,2)
                % identify the position of the considered period within the list of all periods (required to build the matrix)
                position=find(periods==kk);
                % now create the restriction matrix: this will depend on the type of restriction
                % if it is a positive sign restriction...
                if strcmp(signrestable{ii,jj},'+')
                    % ... then input a 1 entry in the corresponding S matrix
                    Scell{1,jj}=[Scell{1,jj};zeros(1,n*nperiods)];
                    Scell{1,jj}(end,(position-1)*n+ii)=1;
                    % if it is a negative sign restriction...
                elseif strcmp(signrestable{ii,jj},'-')
                    % ... then input a -1 entry in the corresponding S matrix
                    Scell{1,jj}=[Scell{1,jj};zeros(1,n*nperiods)];
                    Scell{1,jj}(end,(position-1)*n+ii)=-1;
                    % if it is a zero restriction...
                elseif strcmp(signrestable{ii,jj},'0')
                    % ... then input a 1 entry in the corresponding Z matrix
                    Zcell{1,jj}=[Zcell{1,jj};zeros(1,n*nperiods)];
                    Zcell{1,jj}(end,(position-1)*n+ii)=1;
                    % else, a non-empty entry being neither a sign nor a zero restriction has to be a magnitude restriction
                else
                    % fill the corresponding M matrices:
                    % input a 1 in M
                    Mcell{1,jj}=[Mcell{1,jj};zeros(1,n*nperiods)];
                    Mcell{1,jj}(end,(position-1)*n+ii)=1;
                    % input the lower value of the interval in Ml
                    temp=str2num(signrestable{ii,jj});
                    Mlcell{1,jj}=[Mlcell{1,jj};temp(1,1)];
                    % input the upper value of the interval in Mu
                    Mucell{1,jj}=[Mucell{1,jj};temp(1,2)];
                end
            end
        end
    end
end

% now check what kind of restrictions apply among sign, zero and magnitude restrictions
% check for sign restrictions: if there are any, at least one entry in the cell Scell is non-empty
if sum(~cellfun(@isempty,Scell))~=0
    signres=1;
else
    signres=0;
end
% similarly check for zero restrictions
if sum(~cellfun(@isempty,Zcell))~=0
    zerores=1;
else
    zerores=0;
end
% and finally, check for magnitude restrictions
if sum(~cellfun(@isempty,Mcell))~=0
    magnres=1;
else
    magnres=0;
end

% create the cell aray that will store the values from the simulations
irf_record_allt=cell(n,n);
storage1=cell(It-Bu,T);

% loop over sample periods
for tt=1:T

    % loop over iterations
    parfor ii=1:It-Bu
        % initiate the variable 'success'; this variable will be used to check whether the restrictions are satisfied
        % if there are only zero restrictions, they will be satisfied by construction, and 'success' will simply be ignored
        success=0;
        % how the algorithm will be conducted will depend on the types of restrictions implemented

        % if there are only zero restrictions, the algorithm is simple as no checking is required: the conditions are satisfied by construction
        if zerores==1 && signres==0 && magnres==0
            % draw beta and sigma
            beta=beta_gibbs{tt,1}(:,ii);
            sigma=sigma_t_gibbs{tt,1}(:,:,ii);
            D=chol(bear.nspd(sigma),'lower');
            % obtain orthogonalised IRFs
            [~,ortirfmatrix]=bear.irfsim(beta,D,n,m,p,k,max(IRFperiods,max(periods)));
            % generate the stacked IRF matrix
            stackedirfmat=[];
            for jj=1:numel(periods)
                stackedirfmat=[stackedirfmat;ortirfmatrix(:,:,periods(jj,1)+1)];
            end
            % draw an entire random matrix Q satisfying the zero restrictions
            [Q]=bear.qzerores(n,Zcell,stackedirfmat);
            % there is no need to verify the restrictions: there are satisfied by construction

            % if there are sign/magnitude restrictions, possibly associated with zero restrictions
        else
            % the algorithm becomes a bit more complicated as conditions now need to be checked
            % to maintain efficiency, the algorithm proceeds recursively shock by shock, and stops as soon as a condition on the considered shock fails
            % repeat algorithm for the iteration as long as not all conditions are satisfied
            while success==0
                % switch 'success' to 1; it will be turned back to zero if at any time Q is detected as a candidate not satisfying the restrictions
                success=1;
                % draw randomly the vector of VAR coefficients: draw a random index
                index=floor(rand*(It-Bu))+1;
                % draw beta and sigma
                beta=beta_gibbs{tt,1}(:,index);
                sigma=sigma_t_gibbs{tt,1}(:,:,index);
                D=chol(bear.nspd(sigma),'lower');
                % obtain orthogonalised IRFs
                [~,ortirfmatrix]=bear.irfsim(beta,D,n,m,p,k,max(IRFperiods,max(periods)));
                % generate the stacked IRF matrix
                stackedirfmat=[];
                for jj=1:numel(periods)
                    stackedirfmat=[stackedirfmat;ortirfmatrix(:,:,periods(jj,1)+1)];
                end
                % initiate Qj
                Qj=[];
                % now start looping over the shocks and checking sequentially whether conditions on these shocks hold
                % stop as soon as one restriction fails
                mm=1;
                while success==1 && mm<=n
                    % build column j of the random matrix Q
                    [qj]=bear.qrandj(n,Zcell{1,mm},stackedirfmat,Qj);
                    % obtain the candidate column fj
                    fj=stackedirfmat*qj;
                    % check restrictions: first sign restrictions
                    [success, qj]=bear.checksignres(Scell{1,mm},qj,fj);
                    % if 'success' is still equal to 1, also check for magnitude restrictions
                    if success==1
                        [success]=bear.checkmagres(Mcell{1,mm},Mlcell{1,mm},Mucell{1,mm},fj);
                    end
                    % also, if 'success' is still equal to 1, update Qj by concatenating qj
                    if success==1
                        Qj=[Qj qj];
                    end
                    mm=mm+1;
                end
                % repeat this loop until a succesful draw is obtained
            end
            % with succesful Qj at hand, eventually set Q as Qj
            Q=Qj;
        end

        % store
        for jj=1:IRFperiods
            storage1{ii,tt}(:,:,jj)=ortirfmatrix(:,:,jj)*Q;
        end

    end

end

% reorganise storage
% loop over iterations
for tt=1:T
    for ii=1:It-Bu
        % loop over IRF periods
        for jj=1:IRFperiods
            % loop over variables
            for kk=1:n
                % loop over shocks
                for ll=1:n
                    irf_record_allt{kk,ll}(ii,jj,tt)=storage1{ii,tt}(kk,ll,jj);
                end
            end
        end
    end
end


 function y = mtimes(ob, x)
%function y = mtimes(ob, x)
% A * x
% A' * y
% y' * A
% x' * A'
% B * A		(calls private/fatrix2_mtimes2)
% etc

if isnumeric(ob)
	if isscalar(ob) % scalar * object
		y = fatrix2_scalar_times(ob, x);
	else % row_vector(s) * object
		y = (x' * ob')'; % trick: x <-> ob
	end
return
end

if ~isnumeric(x) % object * object
	if islogical(x)
		warn 'fatrix2 * logical is illogical; reconsider!'
		x = single(x);
%	elseif iscell(x) % untested feature for mai le
		% do nothing here; continue to "ordinary" multiplication below
	else
		y = fatrix2_mtimes2(ob, x);
	return
	end
end

y = fatrix2_mtimes_vector(ob, x); % "ordinary" fatrix * vector multiplication

end % mtimes()

% fatrix2_mtimes_vector()
% ordinary matrix-vector multiplication, but generalized to support arrays
% full multiplication
% y = scale * diag(odiag) * ob * diag(idiag) * x;
function y = fatrix2_mtimes_vector(ob, x)

fun_forw = @(ob, x) fatrix2_do_forw(ob, x);

[nd np] = size(ob);
dimx = size(x);
if dimx(1) == np % [np (L)] vector mode
	diml = [dimx(2:end) 1];
	LL = prod(diml);
	if LL == 1 % usual single column vector case
		x = iembed(ob, x); % [idim]
		y = fun_forw(ob, x); % [odim]
		y = y(:); % [*odim 1]
		y = oselect(ob, y); % [nd 1]
	elseif ob.does_many % multiple column vector without loop
		x = reshape(x, [np LL]); % [np *L]
		x = iembed(ob, x); % [idim *L]
		y = fun_forw(ob, x); % [odim *L]
		y = reshape(y, [prod(ob.odim) LL]); % [*odim *L]
		y = oselect(ob, y); % [nd *L]
		y = reshape(y, [nd diml]); % [nd (L)]
	else % multiple column vector case with loop
		x = reshape(x, [np LL]); % [np *L]
		tmp = iembed(ob, x(:,1)); % [idim]
		tmp = fun_forw(ob, tmp); % [odim]
		y = zeros([prod(ob.odim) LL], class(tmp)); % [*odim *L]
		y(:,1) = tmp(:);
		for ll=2:LL
			tmp = iembed(ob, x(:,ll)); % [idim]
			tmp = fun_forw(ob, tmp); % [odim]
			y(:,ll) = tmp(:);
		end
		y = oselect(ob, y); % [nd *L]
		y = reshape(y, [nd diml]); % [nd (L)]
	end

elseif first_dim_match(dimx, ob.idim)

	diml = [dimx((numel(ob.idim)+1):end) 1];
	LL = prod(diml);
	if LL == 1 % a single array
		y = fun_forw(ob, x); % [odim]
	elseif ob.does_many
		x = reshape(x, [ob.idim LL]); % [idim *L]
		y = fun_forw(ob, x); % [odim *L]
		y = reshape(y, [ob.odim diml]); % [odim (L)]
	else % loop
		x = reshape(x, [prod(ob.idim) LL]); % [*idim *L]
		tmp = reshape(x(:,1), [ob.idim 1]); % [idim]
		tmp = fun_forw(ob, tmp); % [odim]
		y = zeros([prod(ob.odim) LL], class(tmp)); % [*odim *L]
		y(:,1) = tmp(:);
		for ll=2:LL
			tmp = reshape(x(:,ll), [ob.idim 1]); % [idim]
			tmp = fun_forw(ob, tmp); % [odim]
			y(:,ll) = tmp(:);
		end
		y = reshape(y, [ob.odim diml]); % [odim (L)]
	end

else
	pr size(x)
	pr ob.idim
	fail 'input dimension size mismatch'
end

end % fatrix2_mtimes_vector()

% first_dim_match()
% see if first dimensions of dimx match idim properly
% basically, dimx = [idim (L)]
function yn = first_dim_match(dimx, idim)
yn = numel(dimx) >= numel(idim) && isequal(dimx(1:numel(idim)), idim);
if ~yn && idim(end) == 1 % handle case [... 1]
	yn = first_dim_match(dimx, idim(1:end-1));
end
end % first_dim_match()


function [granger, v, n] = ft_connectivity_granger(H, Z, S, varargin)

% FT_CONNECTIVITY_GRANGER computes spectrally resolved granger causality. This
% implementation is loosely based on the code used in Brovelli, et. al., PNAS 101,
% 9849-9854 (2004).
%
% Use as
%   [granger, v, n] = ft_connectivity_granger(H, Z, S, ...)
%
% The input data should be
%   H = spectral transfer matrix, Nrpt x Nchan x Nchan x Nfreq (x Ntime),
%       or Nrpt x Nchancmb x Nfreq (x Ntime). Nrpt can be 1.
%   Z = the covariance matrix of the noise, Nrpt x Nchan x Nchan (x Ntime),
%       or Nrpt x Nchancmb (x Ntime).
%   S = the cross-spectral density matrix with the same dimensionality as H.
%
% Additional optional input arguments come as key-value pairs:
%   'dimord'  = required string specifying how to interpret the input data
%               supported values are 'rpt_chan_chan_freq(_time) and
%               'rpt_chan_freq(_time), 'rpt_pos_pos_freq(_time)' and
%               'rpt_pos_freq(_time)'
%   'method'  = 'granger' (default), or 'instantaneous', or 'total'
%   'hasjack' = boolean, specifying whether the input contains leave-one-outs,
%               required for correct variance estimate (default = false)
%   'powindx' = is a variable determining the exact computation, see below
%
% If the inputdata is such that the channel-pairs are linearly indexed, granger
% causality is computed per quadruplet of consecutive entries, where the convention
% is as follows:
%
%  H(:, (k-1)*4 + 1, :, :, :) -> 'chan1-chan1'
%  H(:, (k-1)*4 + 2, :, :, :) -> 'chan1->chan2'
%  H(:, (k-1)*4 + 3, :, :, :) -> 'chan2->chan1'
%  H(:, (k-1)*4 + 4, :, :, :) -> 'chan2->chan2'
%
% The same holds for the Z and S matrices.
%
% Pairwise block-granger causality can be computed when the inputdata has
% dimensionality Nchan x Nchan. In that case 'powindx' should be specified, as a 1x2
% cell-array indexing the individual channels that go into each 'block'.
%
% See also CONNECTIVITY, FT_CONNECTIVITYANALYSIS

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Undocumented option: powindx can be a struct. In that case, blockwise
% conditional granger can be computed.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Copyright (C) 2009-2017, Jan-Mathijs Schoffelen
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip 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.
%
%    FieldTrip 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 FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

% check for a supported dimord
ft_checkopt(varargin, 'dimord', 'char');

method  = ft_getopt(varargin, 'method',  'granger');
hasjack = ft_getopt(varargin, 'hasjack', false);
powindx = ft_getopt(varargin, 'powindx');
dimord  = ft_getopt(varargin, 'dimord');

%FIXME speed up code and check
siz = size(H);
if numel(siz)==4
  siz(5) = 1;
end
n   = siz(1);
Nc  = siz(2);

outsum = zeros(siz(2:end));
outssq = zeros(siz(2:end));

% crossterms are described by chan_chan_therest
issquare = length(strfind(dimord, 'chan'))==2 || length(strfind(dimord, 'pos'))==2;

switch method
  case 'granger'

    if issquare && isempty(powindx)
      %%%%%%%%%%%%%%%%%%%%%%%%%%%%
      % data are chan_chan_therest
      %%%%%%%%%%%%%%%%%%%%%%%%%%%%

      for kk = 1:n
        for ii = 1:Nc
          for jj = 1:Nc
            if ii ~=jj
              zc     = reshape(Z(kk,jj,jj,:) - Z(kk,ii,jj,:).^2./Z(kk,ii,ii,:),[1 1 1 1 siz(5)]);
              zc     = repmat(zc,[1 1 1 siz(4) 1]);
              numer  = reshape(abs(S(kk,ii,ii,:,:)),[1 1 siz(4:end)]);
              denom  = reshape(abs(S(kk,ii,ii,:,:)-zc.*abs(H(kk,ii,jj,:,:)).^2),[1 1 siz(4:end)]);
              outsum(jj,ii,:,:) = outsum(jj,ii,:,:) + log(numer./denom);
              outssq(jj,ii,:,:) = outssq(jj,ii,:,:) + (log(numer./denom)).^2;
            end
          end
          outsum(ii,ii,:,:) = 0; %self-granger set to zero
        end
      end

    elseif ~issquare && isempty(powindx)
      %%%%%%%%%%%%%%%%%%%%%%%%%%
      %data are linearly indexed
      %%%%%%%%%%%%%%%%%%%%%%%%%%

      for j = 1:n
        for k = 1:Nc
          %FIXME powindx is not used here anymore
          %iauto1  = sum(powindx==powindx(k,1),2)==2;
          %iauto2  = sum(powindx==powindx(k,2),2)==2;
          %icross1 = k;
          %icross2 = sum(powindx==powindx(ones(Nc,1)*k,[2 1]),2)==2;

          % The following is based on hard-coded assumptions: which is fair
          % to do if the order of the labelcmb is according to the output of
          % ft_connectivity_csd2transfer
          if mod(k-1, 4)==0
            continue; % auto granger set to 0
            %iauto1=k;iauto2=k;icross1=k;icross2=k;
          elseif mod(k-1, 4)==1
            iauto1=k+2;iauto2=k-1;icross1=k;icross2=k+1;
          elseif mod(k-1, 4)==2
            iauto1=k-2;iauto2=k+1;icross1=k;icross2=k-1;
          elseif mod(k-1, 4)==3
            continue; % auto granger set to 0
            %iauto1=k;iauto2=k;icross1=k;icross2=k;
          end

          zc      = Z(j,iauto2,:,:) - Z(j,icross1,:,:).^2./Z(j,iauto1,:,:);
          numer   = abs(S(j,iauto1,:,:));
          denom   = abs(S(j,iauto1,:,:)-zc(:,:,ones(1,size(H,3)),:).*abs(H(j,icross1,:,:)).^2);
          outsum(icross2,:,:) = outsum(icross2,:,:) + reshape(log(numer./denom), [1 siz(3:end)]);
          outssq(icross2,:,:) = outssq(icross2,:,:) + reshape((log(numer./denom)).^2, [1 siz(3:end)]);
        end
      end

    elseif issquare && iscell(powindx)
      %%%%%%%%%%%%%%%%%%%
      % blockwise granger
      %%%%%%%%%%%%%%%%%%%

      % H = transfer function nchan x nchan x nfreq
      % Z = noise covariance  nchan x nchan
      % S = crosspectrum      nchan x nchan x nfreq
      % powindx{k} is a list of indices for block k

      nblock = numel(powindx);
      n      = size(H,1);
      nfreq  = size(H,4);

      outsum = zeros(nblock,nblock,nfreq);
      outssq = zeros(nblock,nblock,nfreq);

      for k = 1:nblock
        for m = (k+1):nblock
          indx  = [powindx{k}(:);powindx{m}(:)];
          indx  = cat(1,indx,setdiff((1:size(Z,2))',indx));
          n1    = numel(powindx{k});
          n2    = numel(powindx{m});
          ntot  = numel(indx);
          indx1 = 1:n1;
          indx1_  = (1:ntot)'; indx1_(indx1) = [];
          indx2   = n1+(1:n2);
          indx12_ = (1:ntot)'; indx12_([indx1(:);indx2(:)]) = [];

          for kk = 1:n
            tmpZ = reshape(Z(kk,indx,indx), [ntot ntot]);

            % projection matrix for therest+block2 -> block1
            P1 = [eye(n1)                                zeros(n1,ntot-n1);
              -tmpZ(indx1_,indx1)/tmpZ(indx1,indx1)      eye(ntot-n1)];

            % projection matrix for therest+block1 -> block2
            P2 = [  eye(n1)    -tmpZ(indx1,indx2)/tmpZ(indx2,indx2) zeros(n1,ntot-n1-n2);
              zeros(n2,n1)   eye(n2) zeros(n2, ntot-n1-n2);
              zeros(ntot-n1-n2,n1) -tmpZ(indx12_,indx2)/tmpZ(indx2,indx2) eye(ntot-n1-n2)];

            % invert only once
            for jj = 1:nfreq
              % post multiply transfer matrix with the inverse of the projection matrix
              % this is equivalent to time domain pre multiplication with P
              Sj = reshape(S(kk,indx,indx,jj), [ntot ntot]);
              Zj = tmpZ; %(:,:);
              H1 = reshape(H(kk,indx,indx,jj), [ntot ntot])/P1;
              H2 = reshape(H(kk,indx,indx,jj), [ntot ntot])/P2;
              num1 = abs(det(Sj(indx1,indx1))); % numerical round off leads to tiny imaginary components
              num2 = abs(det(Sj(indx2,indx2))); % numerical round off leads to tiny imaginary components
              denom1 = abs(det(H1(indx1,indx1)*Zj(indx1,indx1)*H1(indx1,indx1)'));
              denom2 = abs(det(H2(indx2,indx2)*Zj(indx2,indx2)*H2(indx2,indx2)'));
              %rH1 = real(H1(indx1,indx1));
              %rH2 = real(H2(indx2,indx2));
              %iH1 = imag(H1(indx1,indx1));
              %iH2 = imag(H2(indx2,indx2));
              %h1 = rH1*Zj(indx1,indx1)*rH1' + iH1*Zj(indx1,indx1)*iH1';
              %h2 = rH2*Zj(indx2,indx2)*rH2' + iH2*Zj(indx2,indx2)*iH2';
              %denom1 = abs(det(h1));
              %denom2 = abs(det(h2));

              outsum(m,k,jj) = log( num1./denom1 )    + outsum(m,k,jj);
              outsum(k,m,jj) = log( num2./denom2 )    + outsum(k,m,jj);
              outssq(m,k,jj) = log( num1./denom1 ).^2 + outssq(m,k,jj);
              outssq(k,m,jj) = log( num2./denom2 ).^2 + outssq(k,m,jj);
            end
          end

        end
      end

    elseif ~issquare && isstruct(powindx) && isfield(powindx, 'n')
      %%%%%%%%%%%%%%%%%%%%%%
      %blockwise conditional
      %%%%%%%%%%%%%%%%%%%%%%

      n     = size(H,1);
      ncmb  = size(H,2);
      nfreq = size(H,3);
      ncnd  = size(powindx.cmbindx,1);

      outsum = zeros(ncnd, nfreq);
      outssq = zeros(ncnd, nfreq);
      for k = 1:n
        tmpS = reshape(S, [ncmb nfreq]);
        tmpH = reshape(H, [ncmb nfreq]);
        tmpZ = reshape(Z, [ncmb 1]);
        tmp  = blockwise_conditionalgranger(tmpS,tmpH,tmpZ,powindx.cmbindx,powindx.n);

        outsum = outsum + tmp;
        outssq = outssq + tmp.^2;
      end

    elseif ~issquare && isstruct(powindx)
      %%%%%%%%%%%%%%%%%%%%%%
      %triplet conditional
      %%%%%%%%%%%%%%%%%%%%%%

      % decode from the powindx struct which rows in the data correspond with
      % the triplets, and which correspond with the duplets
      ublockindx = unique(powindx.blockindx);
      nperblock  = zeros(size(ublockindx));
      for k = 1:numel(ublockindx)
        nperblock(k,1) = sum(powindx.blockindx==ublockindx(k));
      end
      if ~all(ismember(nperblock,[4 9]))
        error('the data should be a mixture of trivariate and bivariate decompositions');
      end
      indx_triplets = ismember(powindx.blockindx, ublockindx(nperblock==9)); ntriplets = sum(indx_triplets)./9;
      indx_duplets  = ismember(powindx.blockindx, ublockindx(nperblock==4)); nduplets  = sum(indx_duplets)./4;

      % this assumes well-behaved powindx.cmbindx
      cmbindx2 = reshape(powindx.cmbindx(indx_duplets, 1), 2, [])';
      cmbindx3 = reshape(powindx.cmbindx(indx_triplets,1), 3, [])';

      cmbindx2 = cmbindx2(1:2:end,:);
      cmbindx3 = cmbindx3(1:3:end,:);

      cmbindx  = powindx.outindx;

      n   = size(H,1);
      siz = size(H);

      outsum = zeros(size(cmbindx,1), size(H,3));
      outssq = outsum;

      % call the low-level function
      for k = 1:n
        H3 = reshape(H(k,indx_triplets,:,:), [3 3 ntriplets siz(3:end)]);
        Z3 = reshape(Z(k,indx_triplets),     [3 3 ntriplets]);

        H2 = reshape(H(k,indx_duplets,:,:), [2 2 nduplets siz(3:end)]);
        Z2 = reshape(Z(k,indx_duplets),     [2 2 nduplets]);

        tmp  = triplet_conditionalgranger(H3,Z3,cmbindx3,H2,Z2,cmbindx2,cmbindx);

        outsum = outsum + tmp;
        outssq = outssq + tmp.^2;
      end

    end

  case 'instantaneous'

    if issquare && isempty(powindx)
      % data are chan_chan_therest
      for kk = 1:n
        for ii = 1:Nc
          for jj = 1:Nc
            if ii ~=jj
              zc1    = reshape(Z(kk,jj,jj,:) - Z(kk,ii,jj,:).^2./Z(kk,ii,ii,:),[1 1 1 1 siz(5)]);
              zc2    = reshape(Z(kk,ii,ii,:) - Z(kk,jj,ii,:).^2./Z(kk,jj,jj,:),[1 1 1 1 siz(5)]);
              zc1    = repmat(zc1,[1 1 1 siz(4) 1]);
              zc2    = repmat(zc2,[1 1 1 siz(4) 1]);
              term1  = abs(S(kk,ii,ii,:,:)) - zc1.*abs(H(kk,ii,jj,:,:)).^2;
              term2  = abs(S(kk,jj,jj,:,:)) - zc2.*abs(H(kk,jj,ii,:,:)).^2;
              numer  = term1.*term2;
              denom  = abs(S(kk,ii,ii,:,:).*S(kk,jj,jj,:,:) - S(kk,ii,jj,:,:).*S(kk,jj,ii,:,:));
              outsum(jj,ii,:,:) = outsum(jj,ii,:,:) + reshape(log(numer./denom),  [1 1 siz(4:end)]);
              outssq(jj,ii,:,:) = outssq(jj,ii,:,:) + reshape((log(numer./denom)).^2, [1 1 siz(4:end)]);
            end
          end
          outsum(ii,ii,:,:) = 0; %self-granger set to zero
        end
      end
    elseif ~issquare && isempty(powindx)
      % data are linearly indexed
      for j = 1:n
        for k = 1:Nc
          %iauto1  = sum(powindx==powindx(k,1),2)==2;
          %iauto2  = sum(powindx==powindx(k,2),2)==2;
          %icross1 = k;
          %icross2 = sum(powindx==powindx(ones(Nc,1)*k,[2 1]),2)==2;
          if mod(k-1, 4)==0
            continue; % auto granger set to 0
            %iauto1=k;iauto2=k;icross1=k;icross2=k;
          elseif mod(k-1, 4)==1
            iauto1=k+2;iauto2=k-1;icross1=k;icross2=k+1;
          elseif mod(k-1, 4)==2
            iauto1=k-2;iauto2=k+1;icross1=k;icross2=k-1;
          elseif mod(k-1, 4)==3
            continue; % auto granger set to 0
            %iauto1=k;iauto2=k;icross1=k;icross2=k;
          end

          zc1     = Z(j,iauto1,:, :) - Z(j,icross2,:, :).^2./Z(j,iauto2,:, :);
          zc2     = Z(j,iauto2,:, :) - Z(j,icross1,:, :).^2./Z(j,iauto1,:, :);
          term1   = abs(S(j,iauto2,:,:)) - zc1(:,:,ones(1,size(H,3)),:).*abs(H(j,icross2,:,:)).^2;
          term2   = abs(S(j,iauto1,:,:)) - zc2(:,:,ones(1,size(H,3)),:).*abs(H(j,icross1,:,:)).^2;
          numer   = term1.*term2;
          denom   = abs(S(j,iauto1,:,:).*S(j,iauto2,:,:) - S(j,icross1,:,:).*S(j,icross2,:,:));

          outsum(icross2,:,:) = outsum(icross2,:,:) + reshape(log(numer./denom), [1 siz(3:end)]);
          outssq(icross2,:,:) = outssq(icross2,:,:) + reshape((log(numer./denom)).^2, [1 siz(3:end)]);
        end
      end
    elseif issquare && iscell(powindx)
      % blockwise granger
      % H = transfer function nchan x nchan x nfreq
      % Z = noise covariance  nchan x nchan
      % S = crosspectrum      nchan x nchan x nfreq
      % powindx{1} is a list of indices for block1
      % powindx{2} is a list of indices for block2
      ft_error('instantaneous causality is not implemented for blockwise factorizations');
    elseif isstruct(powindx)
      %blockwise conditional
      ft_error('blockwise conditional instantaneous causality is not implemented');
    else
      ft_error('not implemented');
    end

  case 'total'

    if issquare && isempty(powindx)
      % data are chan_chan_therest
      for kk = 1:n
        for ii = 1:Nc
          for jj = 1:Nc
            if ii ~=jj
              numer  = abs(S(kk,ii,ii,:,:).*S(kk,jj,jj,:,:));
              denom  = abs(S(kk,ii,ii,:,:).*S(kk,jj,jj,:,:) - S(kk,ii,jj,:,:).*S(kk,jj,ii,:,:));
              outsum(jj,ii,:,:) = outsum(jj,ii,:,:) + reshape(log(numer./denom), [1 1 siz(4:end)]);
              outssq(jj,ii,:,:) = outssq(jj,ii,:,:) + reshape((log(numer./denom)).^2, [1 1 siz(4:end)]);
            end
          end
          outsum(ii,ii,:,:) = 0; %self-granger set to zero
        end
      end
    elseif ~issquare && isempty(powindx)
      % data are linearly indexed
      for j = 1:n
        for k = 1:Nc
          %iauto1  = sum(powindx==powindx(k,1),2)==2;
          %iauto2  = sum(powindx==powindx(k,2),2)==2;
          %icross1 = k;
          %icross2 = sum(powindx==powindx(ones(Nc,1)*k,[2 1]),2)==2;
          if mod(k-1, 4)==0
            continue; % auto granger set to 0
            %iauto1=k;iauto2=k;icross1=k;icross2=k;
          elseif mod(k-1, 4)==1
            iauto1=k+2;iauto2=k-1;icross1=k;icross2=k+1;
          elseif mod(k-1, 4)==2
            iauto1=k-2;iauto2=k+1;icross1=k;icross2=k-1;
          elseif mod(k-1, 4)==3
            continue; % auto granger set to 0
            %iauto1=k;iauto2=k;icross1=k;icross2=k;
          end

          numer   = abs(S(j,iauto1,:,:).*S(j,iauto2,:,:));
          denom   = abs(S(j,iauto1,:,:).*S(j,iauto2,:,:) - S(j,icross1,:,:).*S(j,icross2,:,:));
          outsum(icross2,:,:) = outsum(icross2,:,:) + reshape(log(numer./denom), [1 siz(3:end)]);
          outssq(icross2,:,:) = outssq(icross2,:,:) + reshape((log(numer./denom)).^2, [1 siz(3:end)]);
        end
      end
    elseif issquare && iscell(powindx)
      % blockwise total interdependence

      % S = crosspectrum      nchan x nchan x nfreq
      % powindx{k} is a list of indices for block k

      nblock = numel(powindx);
      n      = size(S,1);
      nfreq  = size(S,4);

      outsum = zeros(nblock,nblock,nfreq);
      outssq = zeros(nblock,nblock,nfreq);

      for k = 1:nblock
        for m = (k+1):nblock
          indx  = [powindx{k}(:);powindx{m}(:)];
          n1    = numel(powindx{k});
          n2    = numel(powindx{m});
          ntot  = n1+n2;
          indx1 = 1:n1;
          indx2 = (n1+1):ntot;

          for kk = 1:n
            for jj = 1:nfreq
              Sj = reshape(S(kk,indx,indx,jj), [ntot ntot]);
              num1 = abs(det(Sj(indx1,indx1))); % numerical round off leads to tiny imaginary components
              num2 = abs(det(Sj(indx2,indx2))); % numerical round off leads to tiny imaginary components
              num  = num1.*num2;
              denom = abs(det(Sj));

              outsum(m,k,jj) = log( num./denom )    + outsum(m,k,jj);
              outsum(k,m,jj) = log( num./denom )    + outsum(k,m,jj);
              outssq(m,k,jj) = log( num./denom ).^2 + outssq(m,k,jj);
              outssq(k,m,jj) = log( num./denom ).^2 + outssq(k,m,jj);
            end
          end

        end
      end

    elseif issquare && isstruct(powindx)
      %blockwise conditional
      ft_error('blockwise conditional total interdependence is not implemented');
    end

  case 'iis'
    ft_warning('THIS IS EXPERIMENTAL CODE, USE AT YOUR OWN RISK!');
    % this is experimental
    if ~issquare && isempty(powindx)
      A = transfer2coeffs(shiftdim(H),(0:size(H,3)-1));
      ncmb = size(A,1)./4;
      iis = coeffs2iis(reshape(A,[2 2 ncmb size(A,2)]),reshape(Z,[2 2 ncmb]));
      iis = repmat(iis(:),[1 4])';
      outsum = iis(:);
      outssq = nan(size(outsum));
    else
      ft_error('iis can only be computed when the input contains sets of bivariate factorizations');
    end

  otherwise
    ft_error('unsupported output requested');
end

granger = outsum./n;
if n>1
  if hasjack
    bias = (n-1).^2;
  else
    bias = 1;
  end
  v = bias*(outssq - (outsum.^2)./n)./(n - 1);
else
  v = [];
end