function slc = resampleScanSlice(scanSetF, scanSetM)
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR 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.
%
% CERR 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 CERR.  If not, see <http://www.gnu.org/licenses/>.

global planC stateS;
indexS = planC{end};

    [xV1, yV1, zV1] = getScanXYZVals(planC{indexS.scan}(scanSetF));
    [xV2, yV2, zV2] = getScanXYZVals(planC{indexS.scan}(scanSetM));

    XYZRes1  = {length(xV1), length(yV1), length(zV1)};

    xLims1 = [min(xV1) max(xV1)];
    yLims1 = [max(yV1) min(yV1)];
    zLims1 = [min(zV1) max(zV1)];

    sliceXVals1 = linspace(xLims1(1), xLims1(2), XYZRes1{1});
    sliceYVals1 = linspace(yLims1(1), yLims1(2), XYZRes1{2});
    sliceZVals1 = linspace(zLims1(1), zLims1(2), XYZRes1{3});

    for i=1:length(sliceZVals1)
        [xM1, yM1, zM1] = meshgrid(sliceXVals1, sliceYVals1, sliceZVals1(i));

        %Apply transformation to the limits if necessary.
        if isfield(planC{indexS.scan}(scanSetM), 'transM')
            mat = [xM1(:) yM1(:) zM1(:) ones(prod(size(xM1)), 1)]';
            mat = inv(planC{indexS.scan}(scanSetM).transM) * mat;
            xM = mat(1,:);
            yM = mat(2,:);
            zM = mat(3,:);
        end

        %Find corners of scan data included in this slice.
        [minX, jnk] = findnearest(xV2, min(xM(:)));
        [jnk, maxX] = findnearest(xV2, max(xM(:)));
        [minY, jnk] = findnearest(yV2, min(yM(:)));
        [jnk, maxY] = findnearest(yV2, max(yM(:)));

        [minZ, jnk] = findnearest(zV2, min(zM(:)));
        [jnk, maxZ] = findnearest(zV2, max(zM(:)));

        %Prepare the x,y,z vector inputs for finterp3.
        xVec = [xV2(1) xV2(2)-xV2(1) xV2(end)];
        yVec = [yV2(1) yV2(2)-yV2(1) yV2(end)];
        zVec = zV2(minZ:maxZ);

        %Interpolate to get slice.
        im = finterp3(xM(:), yM(:), zM(:), planC{indexS.scan}(scanSetM).scanArray(maxY:minY, minX:maxX, minZ:maxZ), xVec, yVec, zVec,0);

        imgSize      = [length(sliceYVals1) length(sliceXVals1) 1];
        im = reshape(im, imgSize);
        im = squeeze(im);
        slc(:,:,i) = im;
    end

end


function [attnInfo] = attnLayerForward(h_t, params, model, attnData, maskInfo)
%
% Attentional Layer: from lstm hidden state to softmax hidden state.
% Input:
%   attnData: require attnData.srcHidVecsOrig and attnData.srcLens
%
% Thang Luong @ 2015, <lmthang@stanford.edu>
%
  attnInfo = [];
  if params.attnGlobal % global
    srcHidVecs = attnData.srcHidVecsOrig;
    % attnInfo.srcMaskedIds = [];
    attnInfo.srcMaskedIds = find(attnData.srcMask(:, 1:params.numSrcHidVecs)'==0); % numSrcHidVecs * curBatchSize
  else % local
    [mu, attnInfo] = regressPositions(model, h_t, attnData.srcLens, params);
    srcPositions = floor(mu);

    % assert
    if params.assert
      assert(isempty(find(srcPositions<1,1)));
      assert(isempty(find(srcPositions(maskInfo.unmaskedIds)>(attnData.srcLens(maskInfo.unmaskedIds)-1),1)));
    end

    % reverse
    if params.isReverse
      srcPositions = params.srcMaxLen - srcPositions;
    end

    % build context vectors
    [srcHidVecs, attnInfo] = buildSrcVecs(attnData.srcHidVecsOrig, srcPositions, maskInfo, attnData.srcLens, params.srcMaxLen, params, attnInfo);

    attnInfo.srcMaskedIds = find(attnInfo.alignMask==0);
  end % end else if attnGlobal

  % compute alignScores: numAttnPositions * curBatchSize
  % TODO: precompute for attnOpt2 and attnOpt3 (we can premultiply srcHidVecs with W_a (attnOpt2) or W_a_src (attnOpt3)
  if params.attnOpt==1 || params.attnOpt==2 % dot product or general dot product
    if params.attnOpt==1 % dot product
      [alignScores] = srcCompareLayerForward(srcHidVecs, h_t, params);
    elseif params.attnOpt==2 % general dot product
      attnInfo.transform_ht = model.W_a * h_t; % TODO: shift the multiplication to srcHidVecs
      [alignScores] = srcCompareLayerForward(srcHidVecs, attnInfo.transform_ht, params);
    end
  elseif params.attnOpt==3 % Bengio's style
    % f(H_src + W_a*h_t): lstmSize * (curBatchSize * numAttnPositions))
    attnInfo.src_ht_hid = reshape(params.nonlinear_f(bsxfun(@plus, srcHidVecs, model.W_a*h_t)), params.lstmSize, []);

    % v_a * src_ht_hid
    alignScores = linearLayerForward(model.v_a, attnInfo.src_ht_hid); % 1 * (curBatchSize * numAttnPositions)
    alignScores = reshape(alignScores, params.curBatchSize, params.numAttnPositions)'; % numAttnPositions * curBatchSize
  end

  if params.attnGlobal == 0 && params.normLocalAttn % new approach for local attention after EMNLP'15
    [attnInfo.distWeights, attnInfo.scaleX] = distLayerForward(mu, attnInfo, params); % numAttnPositions*curBatchSize
    attnInfo.unNormAlignWeights =  alignScores .* attnInfo.distWeights; % weighted by distances
    attnInfo.alignWeights = normLayerForward(attnInfo.unNormAlignWeights, attnInfo.srcMaskedIds);
    attnInfo.alignScores = alignScores;
  else
    % normalize -> alignWeights
    attnInfo.alignWeights = normLayerForward(alignScores, attnInfo.srcMaskedIds);

    % local, regression, multiply with distWeights
    if params.attnGlobal == 0
      [attnInfo.distWeights, attnInfo.scaleX] = distLayerForward(mu, attnInfo, params); % numAttnPositions*curBatchSize
      attnInfo.preAlignWeights = attnInfo.alignWeights;
      attnInfo.alignWeights = attnInfo.preAlignWeights.* attnInfo.distWeights; % weighted by distances
    end
  end

  % assert
  if params.assert
    assert(computeSum(attnInfo.alignWeights(attnInfo.srcMaskedIds), params.isGPU)==0);
  end

  attnInfo.alignWeights(:, maskInfo.maskedIds) = 0;
  % alignWeights, srcHidVecs -> contextVecs
  [contextVecs] = contextLayerForward(attnInfo.alignWeights, srcHidVecs, maskInfo.unmaskedIds, params);

  % f(W_h*[context_t; h_t])
  attnInfo.input = [contextVecs; h_t];
  attnInfo.h_t = h_t;
  attnInfo.softmax_h = hiddenLayerForward(model.W_h, attnInfo.input, params.nonlinear_f);

  % assert
  if params.assert
    assert(isequal(size(attnInfo.alignWeights), [params.numAttnPositions, params.curBatchSize]));
    assert(isequal(size(h_t), size(contextVecs))); % lstmSize * curBatchSize
  end
end

function [mu, h2sInfo] = regressPositions(model, h_t, srcLens, params)
  % h_t -> scales=sigmoid(v_pos*f(W_pos*h_t)) in [0, 1]
  [h2sInfo.scales, h2sInfo.posForwData] = scaleLayerForward(model.W_pos, model.v_pos, h_t, params);

  % scales -> srcPositions
  mu = h2sInfo.scales.*(srcLens-1) + 1;
end


function prtUtilGraphExplore(self)

locs = self.plotLocations;
graph = self.graph;
names = self.nodeNames;

if isempty(locs)
    self = self.optimizePlotLocations;
    locs = self.plotLocations;
end

hF = figure;
hA = axes;
axis(hA,[-.5 .5 -.5 .5]);

nLines = 500;
lineRndStdDev = 0;

self.plot;

hz = zoom;
hp = pan;
set(hp,'ActionPostCallback',@(e,v)mypostcallback);
set(hz,'ActionPostCallback',@(e,v)mypostcallback);
set(hp,'ActionPreCallback',@(e,v)myprecallback);
set(hz,'ActionPreCallback',@(e,v)myprecallback);
hText = [];
hLines = [];

    function myprecallback(e,v)
        try
            delete(hText);
            hText = [];
            delete(hLines);
            hLines = [];
        catch ME
            disp(ME)
        end
    end

    function mypostcallback(e,v)
        % mypostcallback(e,v)
        v = axis;
        in = locs(:,1) > v(1) & locs(:,1) < v(2) & locs(:,2) > v(3) & locs(:,2) < v(4);

        cLocs = locs(in,:);
        cGraph = graph(in,in);
        cD = diag(cGraph);
        cNames = names(in);

        %Choose nLines lines to plot, at random... Should we use degree
        %here? Note, all this is done weirdly to make plotting super fast.
        [linkI,linkJ] = find(cGraph);
        plotLocs = [];
        indices = randperm(length(linkI));
        for i = indices(1:min(length(indices),nLines));
            plotLocs = cat(1,plotLocs,cLocs([linkI(i),linkJ(i)],:),[nan nan]);
        end

        % If there are any connections to plot, plot them:
        if ~isempty(plotLocs)
            plotLocs = plotLocs + randn(size(plotLocs))*lineRndStdDev;
            hold on
            hLines = plot(plotLocs(:,1),plotLocs(:,2),'c');
            uistack(hLines,'bottom');
            set(hLines,'hittest','off');
            hold off;

        end

        % Add the text
        [~,inds] = sort(cD,'descend');
        for i = 1:min([20,length(inds)]);
            hText(i) = text(cLocs(inds(i),1),cLocs(inds(i),2),cNames{inds(i)});
            set(hText(i),'fontsize',10,'fontweight','bold');
            set(hText,'hittest','off');
        end
    end
end


classdef TestVigdergauzMicroStructure < handle

    properties (Access = private)
        designVariable
        unfittedMesh
        volume
        numericalVolume
        computation
        settings
        testName;
    end

    methods (Access = public)

        function obj = TestVigdergauzMicroStructure(cParams)
            obj.init(cParams);
            obj.createUnfittedMesh();
            obj.computeFractionVolume();
        end

        function error = computeError(obj)
            v = obj.volume;
            nv = obj.numericalVolume;
            error = abs(v - nv)/v;
        end

    end

    methods (Access = private)

        function init(obj, cParams)
            obj.volume   = cParams.volume;
            obj.testName = cParams.testName;
            s.testName = obj.testName;
            computer = TopOptComputer(s);
            computer.compute();
            obj.computation = computer.computation;
            obj.settings = computer.settings;
            obj.designVariable = obj.computation.designVariable;
        end

        function createUnfittedMesh(obj)
            meshBackground = obj.computation.designVariable.mesh;
            s.backgroundMesh  = meshBackground.innerMeshOLD;
            s.boundaryMesh    = meshBackground.boxFaceMeshes;
            cParams = SettingsMeshUnfitted(s);
            obj.unfittedMesh = UnfittedMesh(cParams);
            obj.unfittedMesh.compute(obj.designVariable.value); % peta aqui
        end

        function computeFractionVolume(obj)
            v = obj.unfittedMesh.computeMass();
            obj.numericalVolume = v;
        end

    end

end


% put a vector in selected positions of a matrix.
%
function [grad] = B_copyVec2Mat(input_layer, curr_layer, future_layers)
input = input_layer.a;

[D,T,N] = size(input);
D1 = curr_layer.targetDims(1);
D2 = curr_layer.targetDims(2);
index2copy = curr_layer.index2copy;

future_grad = GetFutureGrad(future_layers, curr_layer);

if IsInGPU(input(1))
    grad = gpuArray.zeros(size(input));
else
    output = zeros(size(input));
end

for t=1:T
    for n = 1:N
        tmp = future_grad(:,:,t,n);
        grad(:,t,n) = tmp(index2copy);
    end
end

end


% Defining A Sensor Mask By Opposing Corners Example
%
% This example demonstrates how to define a sensor mask using the grid
% coordinates of two opposing corners of a rectangle, and then use this for
% the detection of the pressure field generated by an initial pressure
% distribution within a two-dimensional homogeneous propagation medium. It
% builds on the Homogeneous Propagation Medium and Using A Binary Sensor
% Mask examples.
%
% author: Bradley Treeby
% date: 21st August 2014
% last update: 21st 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 = 128;           % number of grid points in the x (row) direction
Ny = 128;           % number of grid points in the y (column) direction
dx = 0.1e-3;        % grid point spacing in the x direction [m]
dy = 0.1e-3;        % grid point spacing in the y direction [m]
kgrid = makeGrid(Nx, dx, Ny, dy);

% define the properties of the propagation medium
medium.sound_speed = 1500; 	% [m/s]
medium.alpha_coeff = 0.75;  % [dB/(MHz^y cm)]
medium.alpha_power = 1.5;

% create initial pressure distribution using makeDisc
disc_magnitude = 5; % [Pa]
disc_x_pos = 50;    % [grid points]
disc_y_pos = 50;    % [grid points]
disc_radius = 8;    % [grid points]
disc_1 = disc_magnitude*makeDisc(Nx, Ny, disc_x_pos, disc_y_pos, disc_radius);

disc_magnitude = 3; % [Pa]
disc_x_pos = 80;    % [grid points]
disc_y_pos = 60;    % [grid points]
disc_radius = 5;    % [grid points]
disc_2 = disc_magnitude*makeDisc(Nx, Ny, disc_x_pos, disc_y_pos, disc_radius);

source.p0 = disc_1 + disc_2;

% define the first rectangular sensor region by specifying the location of
% opposing corners
rect1_x_start = 25;
rect1_y_start = 31;
rect1_x_end   = 30;
rect1_y_end   = 50;

% define the second rectangular sensor region by specifying the location of
% opposing corners
rect2_x_start = 71;
rect2_y_start = 81;
rect2_x_end   = 80;
rect2_y_end   = 90;

% assign the list of opposing corners to the sensor mask
sensor.mask = [rect1_x_start, rect1_y_start, rect1_x_end, rect1_y_end;...
               rect2_x_start, rect2_y_start, rect2_x_end, rect2_y_end].';

% run the simulation
sensor_data = kspaceFirstOrder2D(kgrid, medium, source, sensor);

% query the size of the output data - note that when using a sensor mask
% defined using opposing corners, the output data is returned as a
% structure
size(sensor_data(1).p)
size(sensor_data(2).p)

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

% plot a time trace from the centre of each rectangle
figure;
plot(squeeze(sensor_data(1).p(end/2, end/2, :)), 'r-');
hold on;
plot(squeeze(sensor_data(2).p(end/2, end/2, :)), 'b-');
legend('Trace from centre of first rectangle', 'Trace from centre of second rectangle');
xlabel('Time Index');
ylabel('Pressure');
axis tight;


function [stat, cfg] = ft_statistics_crossvalidate(cfg, dat, design)

% FT_STATISTICS_CROSSVALIDATE performs cross-validation using a prespecified
% multivariate analysis given by cfg.mva
%
% Use as
%   stat = ft_timelockstatistics(cfg, data1, data2, data3, ...)
%   stat = ft_freqstatistics    (cfg, data1, data2, data3, ...)
%   stat = ft_sourcestatistics  (cfg, data1, data2, data3, ...)
%
% Options:
%   cfg.mva           = a multivariate analysis (default = {dml.standardizer dml.svm})
%   cfg.statistic     = a cell-array of statistics to report (default = {'accuracy' 'binomial'})
%   cfg.nfolds        = number of cross-validation folds (default = 5)
%   cfg.resample      = true/false; upsample less occurring classes during
%                       training and downsample often occurring classes
%                       during testing (default = false)
%
% Returns:
%   stat.statistic    = the statistics to report
%   stat.model        = the models associated with this multivariate analysis
%
% See also FT_TIMELOCKSTATISTICS, FT_FREQSTATISTICS, FT_SOURCESTATISTICS

% Copyright (c) 2007-2011, F.C. Donders Centre, Marcel van Gerven
%
% 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$

% do a sanity check on the input data
assert(isnumeric(dat),    'this function requires numeric data as input, you probably want to use FT_TIMELOCKSTATISTICS, FT_FREQSTATISTICS or FT_SOURCESTATISTICS instead');
assert(isnumeric(design), 'this function requires numeric data as input, you probably want to use FT_TIMELOCKSTATISTICS, FT_FREQSTATISTICS or FT_SOURCESTATISTICS instead');

cfg.mva       = ft_getopt(cfg, 'mva');
cfg.statistic = ft_getopt(cfg, 'statistic', {'accuracy', 'binomial'});
cfg.nfolds    = ft_getopt(cfg, 'nfolds',   5);
cfg.resample  = ft_getopt(cfg, 'resample', false);
cfg.cv        = ft_getopt(cfg, 'cv', []);
cfg.cv.type   = ft_getopt(cfg.cv, 'type', 'nfold');

% specify classification procedure or ensure it's the correct object
if isempty(cfg.mva)
  cfg.mva = dml.analysis({ dml.standardizer('verbose',true) ...
                           dml.svm('verbose',true)});
elseif ~isa(cfg.mva,'dml.analysis')
  cfg.mva = dml.analysis(cfg.mva);
end

cv_options = {'mva', cfg.mva, 'type', cfg.cv.type, 'resample', cfg.resample, 'compact', true, 'verbose', true};
if strcmp(cfg.cv.type, 'nfold')
  cv_options = cat(2, cv_options, {'folds', cfg.nfolds});
end
cv = dml.crossvalidator(cv_options{:});

if any(isinf(dat(:)))
  ft_warning('Inf encountered; replacing by zeros');
  dat(isinf(dat(:))) = 0;
end

if any(isnan(dat(:)))
  ft_warning('Nan encountered; replacing by zeros');
  dat(isnan(dat(:))) = 0;
end

% perform everything
cv = cv.train(dat',design');

% extract the statistic of interest
s = cv.statistic(cfg.statistic);
for i=1:length(cfg.statistic)
 stat.statistic.(cfg.statistic{i}) = s{i};
end

% get the model averaged over folds
stat.model = cv.model;

fn = fieldnames(stat.model{1});
if any(ismember(fn,  {'weights', 'primal'})),
  selfn = find(ismember(fn, {'weights', 'primal'}));

  % the mean subtraction is needed only once, but speeds up the covariance
  % computation
  dat = bsxfun(@minus, dat, nanmean(dat,2));
  dat_transp = dat.';
  for j=1:numel(selfn)
    % create the 'encoding' matrix from the weights, as per Haufe 2014.
    %covdat = cov(dat');
    for i=1:length(stat.model)
      i
      W = stat.model{i}.(fn{selfn});

      sW   = size(W);
      sdat = size(dat);
      if sW(2)==sdat(1) && sW(1)~=sdat(1)
        W = transpose(W);
      end

      M    = dat'*W;
      covM = cov(M);
      WcovM = (W/covM)./(size(dat,2)-1); % with the correction term for the covariance computation

      %stat.model{i}.(sprintf('%sinv',fn{selfn})) = covdat*W/covM;
      stat.model{i}.(sprintf('%sinv',fn{selfn})) = dat*(dat_transp*WcovM);

    end
  end
end
fn = fieldnames(stat.model{1}); % update the fieldnames, because some might have been added

fn = fieldnames(stat.model{1}); % may now also contain weightsinv
for i=1:length(stat.model)
  for k=1:length(fn)
    if numel(stat.model{i}.(fn{k}))==prod(cfg.dim)
      stat.model{i}.(fn{k}) = reshape(stat.model{i}.(fn{k}),cfg.dim);
    end
  end

end

% required
stat.trial = [];

% add some stuff to the cfg
cfg.cv = cv;


% Fig. 8.4   Feedback Control of Dynamic Systems, 5e
%             Franklin, Powell, Emami
%

clear all;
close all;

zgrid('new')
axis('square')
plot([-1 1],[0 0])
plot([0 0],[-1 1])
title('Fig. 8.4 Natural frequency and damping loci in z-plane')
xlabel('Re(z)')
ylabel('Im(z)')
hold off


function tests = test_beylkin
  tests = functiontests(localfunctions);
end

function test_qmf(testCase)
    f = spx.wavelet.baylkin.quad_mirror_filter();
    verifyTrue(testCase, spx.norm.is_unit_norm_vec(f));
end


function findingVolumeMatch

vS = findVolume('SmoothRectangle');
vR = findVolume('Rectangle');

end

function volV = findVolume(micro)

d = load(['/home/alex/git-repos/SwanLab/Swan/Output/',micro,'/',micro,'.mat']);

var = d.d.variables;

mxV = d.d.domVariables.mxV;
myV = d.d.domVariables.myV;

for imx = 1:length(mxV)
    for imy = 1:length(myV)
        volV(imx,imy) = var{imx,imy}.volume;
    end
end

end


function [varargout]=triThick(varargin)

% function [E,V,Fp1,Fp2]=triThick(Fp,Vp,dirSet,layerThickness,numSteps)
% ------------------------------------------------------------------------
%
% Kevin Mattheus Moerman
% gibbon.toolbox@gmail.com
%
%
% 2019/04/18 Created based on quadThick
% ------------------------------------------------------------------------

%%

[E,V,Fp1,Fp2]=patchThick(varargin{:});

%Collect output
varargout{1}=E;
varargout{2}=V;
varargout{3}=Fp1;
varargout{4}=Fp2;

%%
% _*GIBBON footer text*_
%
% License: <https://github.com/gibbonCode/GIBBON/blob/master/LICENSE>
%
% GIBBON: The Geometry and Image-based Bioengineering add-On. A toolbox for
% image segmentation, image-based modeling, meshing, and finite element
% analysis.
%
% Copyright (C) 2006-2022 Kevin Mattheus Moerman and the GIBBON contributors
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.


function [audio_obj, features] = get_files_and_stft_features(input_dir, l, fn_filter)
    current_dir = cd();
    cd(input_dir);
    files = get_files_by_filter(fn_filter);
    % label according to l-th character in file name
    audio_obj = miraudio(files, 'Label', l, 'Normal');
    a = mirgetdata(audio_obj);
    sr = get(audio_obj, 'Sampling');
    features = cell(length(a), 1);
    for i = 1:length(a)
        f = calc_stft(a{i}, sr{i});
%         f = lpc(a{i});
%         f = analyze_samples(a{i}, sr{i});
        features{i} = f;
%         features{i} = mfcc(a{i}, sr{i}, 512);
    end
    cd(current_dir);
end


%Figure 1.1       Feedback Control of Dynamic Systems, 6e
%                      Franklin, Powell, Emami

% script to generate figure 1.1
clf
sim('fig1_01bsim',24)
plot(roomtemp(:,1),roomtemp(:,2))
axis([0 24 -1 21]);
title('Figure 1.1(b) Plot of room temperature and thermostat setting')
hold on
plot(heatin(:,1),heatin(:,2))
xlabel('Time (hours)');
ylabel('Temperature (degrees F)');
gtext('Room Temperature')
gtext(' Furnace on')
gtext(' Furnace off')
nicegrid;


% select frames from the input stream
%
function grad = B_frame_select(input_layer, future_layers, curr_layer)
input = input_layer.a;
[D,T,N] = size(input);

future_grad = GetFutureGrad(future_layers, curr_layer);

words = ExtractWordsFromString_v2(curr_layer.frameSelect);
selectionType = words{1};
switch selectionType
    case 'last'
        if length(words)>1; nFrameSelect = str2num(words{2}); else nFrameSelect = 1; end
        if N>1; [mask, variableLength] = getValidFrameMask(input_layer); else variableLength = 0; end

        precision = class(gather(future_grad(1,1,1)));
        if strcmpi(class(future_grad), 'gpuArray')
            grad = gpuArray.zeros(D,T, N, precision);
        else
            grad = zeros(D,T, N, precision);
        end
        if variableLength
            last_idx = gather(GetLastValidFrameIndex(mask));
            for i=1:nSeg
                grad(:,(last_idx(i)-nFrameSelect+1):last_idx(i)) = future_grad;
            end
        else
            grad(:,max(1,end-nFrameSelect+1):end,:) = future_grad;
        end
end

end


% VTKQUIVER creates a vtk-file containing a glyph plot similar to Matlab's
%    quiver3 which can be called by a vtk-visualization tool.
%
%    VTKQUIVER(X,Y,Z,U,V,W,VARNAME,FILENAME) plots vectors as glyphs with
%    components (u,v,w) at the points (x,y,z).  The matrices X,Y,Z,U,V,W
%    must all be the same size and contain the corresponding position and
%    velocity components.  VARNAME should contain the description of the
%    visualized variable which is required by the vtk file format. FILENAME
%    is the name of the .vtu-file to store the data.  If the string does
%    not end with '.vtu', this extension is attached.
%
%    VTKQUIVER(X,Y,[],U,V,[],VARNAME,FILENAME) can be used to abbreviate
%    VTKQUIVER(X,Y,zeros(size(X)),U,V,zeros(size(X)),VARNAME,FILENAME), ie.
%    for 2d plots (vtk expects 3d data).
%
%    To visualize the glyphs you have to load the created .vtu-file via
%    paraview and hit the big `Glyph'-button.
%
% Example
%    [X,Y] = meshgrid(-2:0.25:2,-1:0.2:1);
%    Z = X.*exp(-X.^2-Y.^2);
%    [U,V,W] = surfnorm(X,Y,Z);
%    vtkquiver(X,Y,Z,U,V,W,'velocity','vtkquiver.vtu');
%    % open `vtkquiver.vtu' via Paraview or Mayavi2 and choose `Glyph'
%
% See also quiver, quiver3, vtktrisurf
%
% Copyright see license.txt
%
% Author:     Florian Frank
% eMail:      snflfran@gmx.net
% Version:    1.00
% Date:       Jun 16th, 2010

function vtkquiver(x, y, z, u, v, w, varname, filename)

% ASSERTIONS
assert(nargin == 8)
assert(ischar(varname) && ischar(filename))
dim = length(x(:));
if isempty(z); z = zeros(size(x)); end
if isempty(w); w = zeros(size(x)); end
assert(dim == length(y(:)) && dim == length(z(:)) && ...
  dim == length(u(:)) && dim == length(v(:)) && dim == length(w(:)))

% OPEN FILE
if ~strcmp(filename(end-3:end), '.vtu') % append file extension if not specified yet
  filename = [filename '.vtu'];
end

file = fopen(filename, 'wt');

% HEADER
fprintf(file, '<?xml version="1.0"?>\n');
fprintf(file, '<VTKFile type="UnstructuredGrid" version="0.1" byte_order="LittleEndian" compressor="vtkZLibDataCompressor">\n');
fprintf(file, '  <UnstructuredGrid>\n');
fprintf(file, '    <Piece NumberOfPoints="%d" NumberOfCells="%d">\n', dim, 0);

% POINTS
fprintf(file, '      <Points>\n');
fprintf(file, '        <DataArray type="Float32" NumberOfComponents="3" format="ascii">\n');
for k = 1 : dim
  fprintf(file, '          %.3e %.3e %.3e\n', x(k), y(k), z(k));
end
fprintf(file, '        </DataArray>\n');
fprintf(file, '      </Points>\n');

% CELLS (empty but required for paraview)
fprintf(file, '      <Cells>\n');
fprintf(file, '        <DataArray type="Int32" Name="connectivity" format="ascii">\n');
fprintf(file, '        </DataArray>\n');
fprintf(file, '        <DataArray type="Int32" Name="offsets" format="ascii">\n');
fprintf(file, '        </DataArray>\n');
fprintf(file, '        <DataArray type="UInt8" Name="types" format="ascii">\n');
fprintf(file, '        </DataArray>\n');
fprintf(file, '      </Cells>\n');

% % CELL DATA (empty but required for paraview)
% fprintf(file, '      <CellData>\n');
%   fprintf(file, '        <DataArray type="Float32" Name="void" format="ascii">\n');
%   fprintf(file, '        </DataArray>\n');
% fprintf(file, '      </CellData>\n');

% POINT DATA
fprintf(file, '      <PointData Vectors="%s">\n', varname); % def of std value
fprintf(file, '        <DataArray type="Float32" Name="%s" NumberOfComponents="3" format="ascii">\n', varname);
for k = 1 : dim
  fprintf(file, '          %.3e %.3e %.3e\n', u(k), v(k), w(k));
end
fprintf(file, '        </DataArray>\n');
fprintf(file, '      </PointData>\n');

% FOOTER
fprintf(file, '    </Piece>\n');
fprintf(file, '  </UnstructuredGrid>\n');
fprintf(file, '</VTKFile>\n');

fclose(file);

return

end


clear all, close all, clc
figpath = '../FIGURES/'; mkdir(figpath)
datapath = '../DATA/EX_LOTKA_Dependencies/';
addpath('../utils');

%% Paramaters
NARXtraining = 'trainlm'; %'trainlm',trainbr
InputSignalType = 'sphs'; %sphs,sine2

% Noise
% Ntrain_vec = 1000; %1000;
% eta_vec = [0.01 0.05 0.1 0.15 0.2 0.25 0.3 0.35 0.4 0.45 0.5];
% Nr = 50;

% Training length
Ntrain_vec = [5:15,20:5:95,100:100:1000];%,1500:500:3000];
eta_vec = 0;
Nr = 1;

N_ETA = length(eta_vec);
N_LENGTHS = length(Ntrain_vec);
Nmodels = N_ETA*N_LENGTHS;

Nt = 1000; % length(tv);

LOG_SCALE = 1;
%% Load all models
ResultsALL(3) = struct('err', [], 'RelErr', [], 'Ntrain_vec', [], 'DataTrain', [], 'DataValid', [], 'Ttraining', []);

ModelName = 'DMDc';
datapath1 = [datapath,'DMDc/'];
filename = fullfile(datapath1,['EX_LOTKA_SI_',ModelName,'_',InputSignalType,'_Nevol','_STATS.mat']);
load(filename);
ResultsALL(1) = Results;

ModelName = 'SINDYc';
datapath1 = [datapath,'SINDYc/'];
filename = fullfile(datapath1,['EX_LOTKA_SI_',ModelName,'_',InputSignalType,'_Nevol','_STATS.mat']);
load(filename);
ResultsALL(2) = Results;

ModelName = 'NARX';
datapath1 = [datapath,'NARX/',NARXtraining,'/'];
filename = fullfile(datapath1,['EX_LOTKA_SI_',ModelName,'_',InputSignalType,'_Nevol','_STATS.mat']);
load(filename);
ResultsALL(3) = Results;

%% Time series
clear ph
figure, hold on, box on
ccolors = get(gca,'colororder');
iM = 1;
ph(1) = plot(ResultsALL(iM).DataTrain.t, ResultsALL(iM).DataTrain.x(:,1),'-','Color',ccolors(1,:),'LineWidth',1);
ph(2) = plot(ResultsALL(iM).DataTrain.t, ResultsALL(iM).DataTrain.x(:,2),'-','Color',ccolors(2,:),'LineWidth',1);

xlim([0 max(ResultsALL(iM).DataTrain.t)]), ylim([0 100])
xlabel('Time');
ylabel('Population size')
set(gca,'LineWidth',1, 'FontSize',14)
set(gcf,'Position',[100 100 300 150])
set(gcf,'PaperPositionMode','auto'),
print('-depsc2', '-painters', '-loose', '-cmyk', [figpath,'EX_LOTKA_PREDPERF_',InputSignalType,'_TrainingLength_TS','_noleg.eps']);
l1 = legend(ph,'Predator','Prey');
set(l1,'Location','NorthEast')
l1.Position = [l1.Position(1)-0.01 l1.Position(2)+0.02 l1.Position(3)-0.01 l1.Position(4)];
print('-depsc2', '-painters', '-loose', '-cmyk', [figpath,'EX_LOTKA_PREDPERF_',InputSignalType,'_TrainingLength_TS','.eps']);

clear ph
figure, hold on, box on
ccolors = get(gca,'colororder');
iM = 1;
plot(ResultsALL(iM).DataTrain.x(:,1), ResultsALL(iM).DataTrain.x(:,2),'-k','LineWidth',1);
axis equal, axis tight, %axis off
set(gca,'xtick', [], 'ytick', [])
% xlim([0 max(ResultsALL(iM).DataTrain.t)]), ylim([0 100])
set(gca,'LineWidth',1, 'FontSize',14)
set(gcf,'Position',[100 100 300 150])
set(gcf,'PaperPositionMode','auto'),
print('-depsc2', '-painters', '-loose', '-cmyk', [figpath,'EX_LOTKA_PREDPERF_',InputSignalType,'_TrainingLength_PhasePlot','.eps']);

clear ph
figure, hold on, box on
ccolors = get(gca,'colororder');
iM = 1;
plot(ResultsALL(iM).DataTrain.t, ResultsALL(iM).DataTrain.u,'-k','LineWidth',1);
axis equal, axis tight, %axis off
set(gca,'xtick', [], 'ytick', [])
% xlim([0 max(ResultsALL(iM).DataTrain.t)]), ylim([0 100])
set(gca,'LineWidth',1, 'FontSize',14)
set(gcf,'Position',[100 100 300 50])
set(gcf,'PaperPositionMode','auto'),
print('-depsc2', '-painters', '-loose', '-cmyk', [figpath,'EX_LOTKA_PREDPERF_',InputSignalType,'_TrainingLength_Actuation','.eps']);

%% Mean squared error
clear ph
% Prediction over training/validation
figure, hold on, box on
ccolors = [1 0 0; 0 1 0; 0.7,0.7,1];
for iM = 1:3
    ph(iM) = plot(ResultsALL(iM).DataTrain.t(ResultsALL(iM).Ntrain_vec), ResultsALL(iM).err(:,1),'-','Color',ccolors(iM,:),'LineWidth',1);
    plot(ResultsALL(iM).DataTrain.t(ResultsALL(iM).Ntrain_vec), ResultsALL(iM).err(:,2),'--','Color',ccolors(iM,:),'LineWidth',1);
end

xlim([0 max(ResultsALL(iM).DataTrain.t)]), ylim([0 12000])
xlabel('Training Time');
ylabel('MSE')
set(gca,'LineWidth',1, 'FontSize',14)
set(gcf,'Position',[100 100 300 150])
set(gcf,'PaperPositionMode','auto'),
print('-depsc2', '-painters', '-loose', '-cmyk', [figpath,'EX_LOTKA_PREDPERF_',InputSignalType,'_TrainingLength_MSE','_noleg.eps']);
l1 = legend(ph,'DMDc','SINDYc','NARX');
set(l1,'Location','NorthEast')
l1.Position = [l1.Position(1)-0.01 l1.Position(2)+0.02 l1.Position(3)-0.01 l1.Position(4)];
print('-depsc2', '-painters', '-loose', '-cmyk', [figpath,'EX_LOTKA_PREDPERF_',InputSignalType,'_TrainingLength_MSE','.eps']);

delete(l1)
xlim([0 30]), ylim([0 2500])
print('-depsc2', '-painters', '-loose', '-cmyk', [figpath,'EX_LOTKA_PREDPERF_',InputSignalType,'_TrainingLength_MSE','_ZOOM.eps']);

% Prediction over training
figure, hold on, box on
ccolors = [1 0 0; 0 1 0; 0.7,0.7,1];
for iM = 1:3
    ph(iM) = plot(ResultsALL(iM).DataTrain.t(ResultsALL(iM).Ntrain_vec), ResultsALL(iM).err(:,1),'-','Color',ccolors(iM,:),'LineWidth',1);
end

xlim([0 max(ResultsALL(iM).DataTrain.t)]), ylim([0 12000])
xlabel('Training Time');
ylabel('MSE')
set(gca,'LineWidth',1, 'FontSize',14)
set(gcf,'Position',[100 100 300 150])
set(gcf,'PaperPositionMode','auto'),
print('-depsc2', '-painters', '-loose', '-cmyk', [figpath,'EX_LOTKA_PREDPERF_',InputSignalType,'_TrainingLength_MSE_train','_noleg.eps']);
l1 = legend(ph,'DMDc','SINDYc','NARX');
set(l1,'Location','NorthEast')
l1.Position = [l1.Position(1)-0.01 l1.Position(2)+0.02 l1.Position(3)-0.01 l1.Position(4)];
print('-depsc2', '-painters', '-loose', '-cmyk', [figpath,'EX_LOTKA_PREDPERF_',InputSignalType,'_TrainingLength_MSE_train','.eps']);

delete(l1)
xlim([0 30]), ylim([0 2500])
print('-depsc2', '-painters', '-loose', '-cmyk', [figpath,'EX_LOTKA_PREDPERF_',InputSignalType,'_TrainingLength_MSE_train','_ZOOM.eps']);

% Prediction over validation
figure, hold on, box on
ccolors = [1 0 0; 0 1 0; 0.7,0.7,1];

for iM = 1:3
    data2plot = ResultsALL(iM).err(:,2);
    if any(isnan(data2plot)==1)
        data2plot(find(isnan(ResultsALL(iM).err(:,2)))) = 10^6;
    end
    ph(iM) = plot(ResultsALL(iM).DataTrain.t(ResultsALL(iM).Ntrain_vec), data2plot,'-','Color',ccolors(iM,:),'LineWidth',1);
end

xlim([0 max(ResultsALL(iM).DataTrain.t)]),

% if log scale
if LOG_SCALE
    ylim([10^-1 5*10^4])
    set(gca,'yscale','log')
    set(gca,'ytick',[10.^[-1:2:4]])
end

xlabel('Training Time');
ylabel('MSE')
set(gca,'LineWidth',1, 'FontSize',14)
set(gcf,'Position',[100 100 300 150])
set(gcf,'PaperPositionMode','auto'),
print('-depsc2', '-painters', '-loose', '-cmyk', [figpath,'EX_LOTKA_PREDPERF_',InputSignalType,'_TrainingLength_MSE_valid','_noleg.eps']);
l1 = legend(ph,'DMDc','SINDYc','NARX');
set(l1,'Location','NorthEast')
l1.Position = [l1.Position(1)-0.01 l1.Position(2)+0.02 l1.Position(3)-0.01 l1.Position(4)];
print('-depsc2', '-painters', '-loose', '-cmyk', [figpath,'EX_LOTKA_PREDPERF_',InputSignalType,'_TrainingLength_MSE_valid','.eps']);

delete(l1)
xlim([0 30]), ylim([0 2500])
print('-depsc2', '-painters', '-loose', '-cmyk', [figpath,'EX_LOTKA_PREDPERF_',InputSignalType,'_TrainingLength_MSE_valid','_ZOOM.eps']);

%% Relative error
clear ph
% Prediction over training/validation
figure, hold on, box on
ccolors = [1 0 0; 0 1 0; 0.7,0.7,1];
for iM = 1:3
    ph(iM) = plot(ResultsALL(iM).DataTrain.t(ResultsALL(iM).Ntrain_vec), ResultsALL(iM).RelErr(:,1),'-','Color',ccolors(iM,:),'LineWidth',1);
    plot(ResultsALL(iM).DataTrain.t(ResultsALL(iM).Ntrain_vec), ResultsALL(iM).RelErr(:,1),'--','Color',ccolors(iM,:),'LineWidth',1);
end

xlim([0 max(ResultsALL(iM).DataTrain.t)]), ylim([0 6])
xlabel('Training Time');
ylabel('Avg. Rel. Error')
set(gca,'LineWidth',1, 'FontSize',14)
set(gcf,'Position',[100 100 300 150])
set(gcf,'PaperPositionMode','auto'),
print('-depsc2', '-painters', '-loose', '-cmyk', [figpath,'EX_LOTKA_PREDPERF_',InputSignalType,'_TrainingLength_RelErr','_noleg.eps']);
l1 = legend(ph,'DMDc','SINDYc','NARX');
set(l1,'Location','NorthEast')
l1.Position = [l1.Position(1)-0.01 l1.Position(2)+0.02 l1.Position(3)-0.01 l1.Position(4)];
print('-depsc2', '-painters', '-loose', '-cmyk', [figpath,'EX_LOTKA_PREDPERF_',InputSignalType,'_TrainingLength_RelErr','.eps']);

delete(l1)
xlim([0 30]), ylim([0 2])
print('-depsc2', '-painters', '-loose', '-cmyk', [figpath,'EX_LOTKA_PREDPERF_',InputSignalType,'_TrainingLength_RelErr','_ZOOM.eps']);

% Prediction over training
figure, hold on, box on
ccolors = [1 0 0; 0 1 0; 0.7,0.7,1];
for iM = 1:3
    ph(iM) = plot(ResultsALL(iM).DataTrain.t(ResultsALL(iM).Ntrain_vec), ResultsALL(iM).RelErr(:,1),'-','Color',ccolors(iM,:),'LineWidth',1);
end

xlim([0 max(ResultsALL(iM).DataTrain.t)]), ylim([0 6])
xlabel('Training Time');
ylabel('Avg. Rel. Error')
set(gca,'LineWidth',1, 'FontSize',14)
set(gcf,'Position',[100 100 300 150])
set(gcf,'PaperPositionMode','auto'),
print('-depsc2', '-painters', '-loose', '-cmyk', [figpath,'EX_LOTKA_PREDPERF_',InputSignalType,'_TrainingLength_RelErr_train','_noleg.eps']);
l1 = legend(ph,'DMDc','SINDYc','NARX');
set(l1,'Location','NorthEast')
l1.Position = [l1.Position(1)-0.01 l1.Position(2)+0.02 l1.Position(3)-0.01 l1.Position(4)];
print('-depsc2', '-painters', '-loose', '-cmyk', [figpath,'EX_LOTKA_PREDPERF_',InputSignalType,'_TrainingLength_RelErr_train','.eps']);

delete(l1)
xlim([0 30]), ylim([0 2])
print('-depsc2', '-painters', '-loose', '-cmyk', [figpath,'EX_LOTKA_PREDPERF_',InputSignalType,'_TrainingLength_RelErr_train','_ZOOM.eps']);

% Prediction over validation
figure, hold on, box on
ccolors = [1 0 0; 0 1 0; 0.7,0.7,1];
for iM = 1:3
    data2plot = ResultsALL(iM).RelErr(:,2);
    if any(isnan(ResultsALL(iM).err(:,2))==1)
        data2plot(find(isnan(ResultsALL(iM).err(:,2)))) = 10^6;
    end
    ph(iM) = plot(ResultsALL(iM).DataTrain.t(ResultsALL(iM).Ntrain_vec), data2plot,'-','Color',ccolors(iM,:),'LineWidth',1);
end

xlim([0 max(ResultsALL(iM).DataTrain.t)]), ylim([0 6])

% if log scale
if LOG_SCALE
    ylim([5*10^-3 10^1])
    set(gca,'yscale','log')
    set(gca,'ytick',[10.^[-3:1:1]])
end

xlabel('Training Time');
ylabel('Avg. Rel. Error')
set(gca,'LineWidth',1, 'FontSize',14)
set(gcf,'Position',[100 100 300 150])
set(gcf,'PaperPositionMode','auto'),
print('-depsc2', '-painters', '-loose', '-cmyk', [figpath,'EX_LOTKA_PREDPERF_',InputSignalType,'_TrainingLength_RelErr_valid','_noleg.eps']);
l1 = legend(ph,'DMDc','SINDYc','NARX');
set(l1,'Location','NorthEast')
l1.Position = [l1.Position(1)-0.01 l1.Position(2)+0.02 l1.Position(3)-0.01 l1.Position(4)];
print('-depsc2', '-painters', '-loose', '-cmyk', [figpath,'EX_LOTKA_PREDPERF_',InputSignalType,'_TrainingLength_RelErr_valid','.eps']);

delete(l1)
xlim([0 30]), ylim([0 2])
print('-depsc2', '-painters', '-loose', '-cmyk', [figpath,'EX_LOTKA_PREDPERF_',InputSignalType,'_TrainingLength_RelErr_valid','_ZOOM.eps']);

%% Training time
clear ph
figure, hold on, box on
ccolors = [1 0 0; 0 1 0; 0.7,0.7,1];
for iM = 1:3
    ph(iM) = plot(ResultsALL(iM).DataTrain.t(ResultsALL(iM).Ntrain_vec), ResultsALL(iM).Ttraining,'-','Color',ccolors(iM,:),'LineWidth',1);
end

xlim([0 max(ResultsALL(iM).DataTrain.t)]), %ylim([0 6])
xlabel('Training Length');
ylabel('Training Time [s]')

% if log scale
set(gca,'yscale','log')
ylim([10^-3 3*10^0])
set(gca,'ytick',[10^-3 10^-2 10^-1 10^0])

set(gca,'LineWidth',1, 'FontSize',14)
set(gcf,'Position',[100 100 300 150])
set(gcf,'PaperPositionMode','auto'),
print('-depsc2', '-painters', '-loose', '-cmyk', [figpath,'EX_LOTKA_PREDPERF_',InputSignalType,'_TrainingLength_TrainTime','_noleg.eps']);
l1 = legend(ph,'DMDc','SINDYc','NARX');
set(l1,'Location','NorthWest')
l1.Position = [l1.Position(1)-0.01 l1.Position(2)+0.02 l1.Position(3)-0.01 l1.Position(4)];
print('-depsc2', '-painters', '-loose', '-cmyk', [figpath,'EX_LOTKA_PREDPERF_',InputSignalType,'_TrainingLength_TrainTime','.eps']);


function p3 = min(p1)
%PREAL/MIN Overloaded MIN function for class preal.
% Note: Unlike datafun/min preal/min returns the smallest single
% element of the array, resulting in one scalar.

global useUnitsFlag

if ~(useUnitsFlag) % If physunits is disabled...
    p3=max(double(p1)); % ... treat as double.
    return
end

p1=preal(p1);
p3=p1(1);
for k=2:numel(p1)
    if p1(k)<p3
        p3=p1(k);
    end
end


function modelRev = convertToReversible(model)
% Converts a model structure from irreversible format to
% reversible format
%
% USAGE:
%
%    modelRev = convertToReversible(model)
%
% INPUT:
%    model:       COBRA model in irreversible format (forward/backward reactions separated)
%                 forward reactions have to be marked with an _f in the id,
%                 backward with an _b. reverse reactions (i.e. reactions
%                 which were originally only allowed to carry negative flux
%                 must have a _r tag at the end of the reaction id.
%                 It is further assumed, that there is an exact 1:1 match
%                 between _f and _b reactions.
%
% OUTPUT:
%    modelRev:    Model in reversible format
%
% .. Author: - Greg Hannum 7/22/05
%
%
%

nRxns = length(model.rxns);
%indicator for reactions already handled
rxnProcessed = false(length(model.rxns),1);
%get the basic names of the reactions.
reactionbasenames = regexprep(model.rxns,'(_f|_r|_b)$','');
%Get the reaction positions which need to be reversed
reversePos = ~cellfun(@isempty ,regexp(model.rxns,'(_r|_b)$'));
%and those, which will be deleted (all backwards. Since backward only
%reactions are marked with an _r, we should always have a _f which is the
%other matching reaction.
reactionsToRemove = ~cellfun(@isempty ,regexp(model.rxns,'(_b)$'));

%and replace the names
model.rxns = reactionbasenames;

%We can directly update the lower bounds and stoichiometries of reverse reactions
%The assumption is, that all reverse reaction bounds and forward reaction
%bounds are consistent.
templbs = model.lb(reversePos);
model.lb(reversePos) = -model.ub(reversePos);
model.ub(reversePos) = -templbs;
model.S(:,reversePos) = -model.S(:,reversePos);

%update all reactions

for i = 1:numel(reactionbasenames)
    if(rxnProcessed(i))
        continue;
    end
    matches = ismember(reactionbasenames,reactionbasenames{i});
    rxnProcessed = rxnProcessed | matches;
    if(sum(matches) > 1)
        %We only need to do something if we have a forward/backward pair.
        %otherwise we don't do anything (since the input format is not
        %matched.
        revpos = reversePos & matches;
        fwpos = ~reversePos & matches;
        model.lb(fwpos) = min(model.lb(revpos), model.lb(fwpos));
        model.ub(fwpos) = max(model.ub(revpos), model.ub(fwpos));
    end
end

%Remove the surplus model fields (they should be the same anyways)
modelRev = removeFieldEntriesForType(model,reactionsToRemove,'rxns',nRxns);

%and remove a potential "match field
if isfield(modelRev, 'match')
    modelRev = rmfield(modelRev,'match');
end
modelRev.reversibleModel = true;


%% Grain Exchange Symmetry
%
%%
% %%
% MTEX allows to identify antipodal directions to model axes and to
% identify misorientations with opposite rotational angle. The later is
% required when working with misorientations between grains of the same
% phase and the order of the grains is arbitrary.


  function [mat nx ny nb na] = wtf_read(file, varargin)
%|function [mat nx ny nb na] = wtf_read(file, [options])
%| read aspire sparse matrix file (usually file.wtf)
%| and return matlab sparse matrix
%| option
%|	'parse'	0|1	if 1, do it by old matlab parsing way (old wtfread.m)
%| Copyright 2008-9-26, Jeff Fessler, University of Michigan

if ~nargin, ir_usage, end
if nargin == 1 && streq(file, 'test'), wtf_read_test, return, end

arg.chat = 0;
arg.parse = 0;
arg = vararg_pair(arg, varargin);

if ~has_mex_jf
	fail('cannot read .wtf due to mex problem')
end

if arg.parse
	[mat nx ny nb na mask] = wtf_read_parse(file, arg.chat);
return
end

[buff nx ny nb na] = wtfmex('asp:read', file, int32(arg.chat));
[mat nx ny nb na is_tranpose] = wtfmex('asp:mat', buff, int32(arg.chat));
if is_tranpose
	mat = mat';
end

%
% wtf_read_parse()
% reads in a sparse matrix from Aspire format
% A is the returned sparse matrix
% n is a structure containing dimensions
% mask is the nx by ny support mask
%

function [A, nx, ny, nb, na, mask, n] = wtf_read_parse(file, chat)

machine = 'native';		% this should work
%machine = 'ieee-be';		% but try this if it doesn't...
fp = fopen(file, 'r', machine);
if (fp == -1), error fopen, end

%
% read until two form feeds
%
while (1)
	c = fread(fp, 1, 'char');
	if chat
		fprintf(1, '%c', char(c))	% echo header
	end
	if isempty(c)
		error eof
	end
	if (c == 12)	% form feed
		c == fread(fp, 1, 'char');
		if (c ~= 12)
			error only one form feed?
		else
			break;
		end
	end
end

%
% read binary header, extract some dimensions from it
%
tmp = fread(fp, 128/4, 'int');
type.group = tmp(1);
if (type.group ~= 1), error(sprintf('type.group = %d', type.group)), end
type.index = tmp(2);
if (type.index ~= 0), error 'type.index', end
type.value = tmp(3);
if (type.value ~= 0), error 'type.value', end
n.wt = tmp(6);
nx = tmp(12);
ny = tmp(13);
nb = tmp(14);
na = tmp(15);
if chat
	printf('nxy=%d,%d nba=%d,%d nwt=%d', ...
		nx, ny, nb, na, n.wt)
end

%
% read support
%
mask = fread(fp, nx*ny, 'uchar');
mask = reshape(mask, nx, ny);
if chat
	imagesc(mask');
end

%
% read length and offset
%
length = fread(fp, nx*ny, 'uint32');
offset = fread(fp, nx*ny, 'uint32');
if any(diff(offset) ~= length(1:end-1)), error bug, end

%
% read index and value arrays
%
index = fread(fp, n.wt, 'uint16');
value = fread(fp, n.wt, 'float32');

c = fread(fp, 1, 'char');
if ~isempty(c), error 'extra stuff in file?', end

%
% close file
%
if (fclose(fp)), error fclose, end

%
% generate sparse matrix
%
i = 1 + index;
j = zeros(n.wt,1);
n.col = nx * ny;
h = waitbar(0, 'Sparse matrix formation');
for ii=1:n.col
	t = [1:length(ii)] + offset(ii);
	j(t) = ii;
	waitbar(ii/n.col)
end
close(h)
if any(i <= 0) || any(j <= 0) || any(i > nb*na) || any(j > n.col)
	error 'bad indices'
end
if chat
	printf('wt value range [%g,%g]', min(value), max(value))
end
A = sparse(i, j, value, nb*na, n.col);

%
% wtf_read_test()
%
function wtf_read_test
nx = 6;
ny = 4;
nb = 8;
na = 5;
rng(0)
A = rand(nb*na, nx*ny);
A = double(single(A));

file = [test_dir 't.wtf'];
for row_grouped = 0:1
	pr row_grouped
	delete(file)
	wtf_write(file, A, nx, ny, nb, na, ...
		'chat', 0, 'row_grouped', row_grouped)
%	eval(['!wt test ' file])

	if 0 % test old "load" use
		[B mx my mb ma is_transpose] = wtfmex('load', file);
		pr is_transpose
		if is_transpose, B = B'; end
		jf_equal(A, B)
		jf_equal([mx my mb ma], [nx ny nb na])
	end

	if 1 % test new "asp:load" use
		[B mx my mb ma is_transpose] = wtfmex('asp:load', file);
		pr is_transpose
		if is_transpose, B = B'; end
		jf_equal(A, B)
		jf_equal([mx my mb ma], [nx ny nb na])
	end

	if 1 % test wtf_read
		[B mx my mb ma] = wtf_read(file);
		jf_equal(A, B)
		jf_equal([mx my mb ma], [nx ny nb na])
	end
end

ig = image_geom('nx', 22, 'ny', 20, 'dx', 2);
sg = sino_geom('par', 'nb', 24, 'na', 18, 'dr', 1.8);
ig.mask = ig.circ(ig.fov/2) > 0;

list = {'col', 'row'};
for ii=1:2
	gtype = list{ii};
	tmp = aspire_pair(sg, ig, 'support', 'array');
	buff = wtfmex('asp:gensys', tmp', gtype, uint8(ig.mask), int32(0));
	[mat mx my mb ma is_transpose] = wtfmex('asp:mat', buff, int32(0));
	jf_equal([mx my mb ma], [ig.nx ig.ny sg.nb sg.na])
	pr is_transpose
	if is_transpose, mat = mat'; end
	jf_equal(size(mat), [sg.nb*sg.na ig.nx*ig.ny])
end

% todo: test parse version?


function g = svargplvmPointGradient(x, model, y)
% VARGPLVMPOINTGRADIENT Wrapper function for gradient of a single point.
% FORMAT
% DESC is a wrapper function for the gradient of the log likelihood
% with respect to a point in the latent space. The GP-LVM
% model is one that is assumed to have already been trained.
% ARG x : the position in the latent space that is being optimised.
% ARG model : the trained GP-LVM model that is being optimised.
% ARG y : the position in data space for which the latent point is
% being optimised.
% RETURN g : the gradient of the log likelihood with respect to the
% latent position.
%
% SEEALSO : vargplvmPointLogLikeGradient, vargplvmOptimisePoint
%
% COPYRIGHT Michalis K. Titsias and Neil D. Lawrence, 2009, 2011
% COPYRIGHT Andreas C. Damianou, 2011

% VARGPLVM

if isfield(model, 'dynamics') && ~isempty(model.dynamics)
    if isfield(model.dynamics, 'reoptimise') && model.dynamics.reoptimise  %%% RE-OPT-CODE-NEW
        [vardistx, model] = vargplvmPartExpand(model, x); %%% RE-OPT-CODE-NEW
    else %%% RE-OPT-CODE-NEW
        % this is doing the expand
        x = reshape(x, model.N+size(y,1), model.dynamics.q*2);
        xtrain = x(1:model.N,:);
        xtest = x(model.N+1:end,:);
        model.dynamics.vardist = vardistExpandParam(model.dynamics.vardist, xtrain);
        vardistx = vardistExpandParam(model.vardistx, xtest);
        % end of expand
    end %%% RE-OPT-CODE-NEW
else
    vardistx = model.vardistx;
    vardistx = vardistExpandParam(vardistx, x);
end

g = - svargplvmPointLogLikeGradient(model, vardistx, y);


function varargout = freq_response_localwfs_vss(X,xs,src,conf)
%FREQ_RESPONSE_LOCALWFS_VSS frequency response for local WFS
%
%   Usage: [S,f] = freq_response_localwfs_vss(X,xs,src,conf)
%
%   Input parameters:
%       X           - listener position / m
%       xs          - position of virtual source / m
%       src         - source type of the virtual source
%                         'pw' -plane wave
%                         'ps' - point source
%                         'fs' - focused source
%       conf        - configuration struct (see SFS_config)
%
%   Output parameters:
%       S           - simulated frequency response
%       f           - corresponding frequency axis / Hz
%
%   FREQ_RESPONSE_LOCALWFS_VSS(X,xs,src,conf) simulates the frequency response
%   of a source synthesized by local WFS using virtual secondary sources (VSS)
%   at the given virtual microphone position X.
%   The length in samples of the frequency response is given by conf.N. The
%   actual calculation is done via sound_field_mono() and a loop over frequency
%   f.
%
%   See also: sound_field_mono_localwfs_vss, time_response_localwfs_vss

%*****************************************************************************
% 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 = 4;
nargmax = 4;
narginchk(nargmin,nargmax);
isargposition(X);
isargxs(xs);
isargchar(src);
isargstruct(conf);

%% ===== Configuration ==================================================
N = conf.N;
showprogress = conf.showprogress;
useplot = conf.plot.useplot;
% Check type of secondary sources to use
if strcmp('2D',conf.dimension)
    greens_function = 'ls';
else
    greens_function = 'ps';
end

%% ===== Computation ====================================================
% Disable progress bar and plotting for sound_field_imp()
conf.showprogress = false;
conf.plot.useplot = false;
% Get the position of the loudspeakers
x0_real = secondary_source_positions(conf);
% Generate frequencies (10^1-10^4.3)
f = logspace(0,4.3,N)';
S = zeros(size(f));
% Get the result for all frequencies
for ii = 1:length(f)
    if showprogress, progress_bar(ii,length(f)); end
    [D, x0] = driving_function_mono_localwfs_vss(x0_real,xs,src,f(ii),conf);
    % Calculate sound field at the listener position
    P = sound_field_mono(X(1),X(2),X(3),x0,greens_function,D,f(ii),conf);
    S(ii) = abs(P);
end

% Return parameter
if nargout>0, varargout{1}=S; end
if nargout>1, varargout{2}=f; end

%% ===== Plotting ========================================================
if nargout==0 || useplot
    figure;
    figsize(conf.plot.size(1),conf.plot.size(2),conf.plot.size_unit);
    semilogx(f,db(S));
    set(gca,'XTick',[10 100 250 1000 5000 20000]);
    ylabel('amplitude / dB');
    xlabel('frequency / Hz');
end


function bF = const_bF_appA(PATH)

% number of segments
m = length(PATH)-1;
b = zeros(10*m,1);

% Start
b(1) = PATH(1);
% end
b(end-4) = PATH(end);
% waypoint positions
for i = 1:m-1
    b(10*(i-1)+6) = PATH(i+1);
    b(10*(i-1)+11) = PATH(i+1);
end

% Permutation
C = permut_mat_appA(m);
b_sorted = C*b;
% number of unknowns
unkns = 8*(m-1);
bF = b_sorted(1:end-unkns);


function varargout = process_granger1n( varargin )
% PROCESS_GRANGER1N: Compute the Granger causality between all the pairs of signals, in one file.

% @=============================================================================
% This function is part of the Brainstorm software:
% https://neuroimage.usc.edu/brainstorm
%
% Copyright (c) University of Southern California & McGill University
% This software is distributed under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPLv3
% license can be found at http://www.gnu.org/copyleft/gpl.html.
%
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%
% For more information type "brainstorm license" at command prompt.
% =============================================================================@
%
% Authors: Francois Tadel, 2012-2020

eval(macro_method);
end

%% ===== GET DESCRIPTION =====
function sProcess = GetDescription() %#ok<DEFNU>
    % Description the process
    sProcess.Comment     = 'Bivariate Granger causality NxN';
    sProcess.Category    = 'Custom';
    sProcess.SubGroup    = 'Connectivity';
    sProcess.Index       = 661;
    sProcess.Description = 'https://neuroimage.usc.edu/brainstorm/Tutorials/Connectivity';
    % Definition of the input accepted by this process
    sProcess.InputTypes  = {'data',     'results',  'matrix'};
    sProcess.OutputTypes = {'timefreq', 'timefreq', 'timefreq'};
    sProcess.nInputs     = 1;
    sProcess.nMinFiles   = 1;

    % === CONNECT INPUT
    sProcess = process_corr1n('DefineConnectOptions', sProcess, 1);
    % === REMOVE EVOKED REPONSE
    sProcess.options.removeevoked.Comment = 'Remove evoked response from each trial';
    sProcess.options.removeevoked.Type    = 'checkbox';
    sProcess.options.removeevoked.Value   = 0;
    sProcess.options.removeevoked.Group   = 'input';
    % === GRANGER ORDER
    sProcess.options.grangerorder.Comment = 'Maximum Granger model order (default=10):';
    sProcess.options.grangerorder.Type    = 'value';
    sProcess.options.grangerorder.Value   = {10, '', 0};
    % === OUTPUT MODE
    sProcess.options.outputmode.Comment = {'Save individual results (one file per input file)', 'Concatenate input files before processing (one file)', 'Save average connectivity matrix (one file)'};
    sProcess.options.outputmode.Type    = 'radio';
    sProcess.options.outputmode.Value   = 1;
    sProcess.options.outputmode.Group   = 'output';
end

%% ===== FORMAT COMMENT =====
function Comment = FormatComment(sProcess) %#ok<DEFNU>
    Comment = sProcess.Comment;
end

%% ===== RUN =====
function OutputFiles = Run(sProcess, sInputA) %#ok<DEFNU>
    % Input options
    OPTIONS = process_corr1n('GetConnectOptions', sProcess, sInputA);
    if isempty(OPTIONS)
        OutputFiles = {};
        return
    end

    % Metric options
    OPTIONS.Method = 'granger';
    OPTIONS.RemoveEvoked = sProcess.options.removeevoked.Value;
    OPTIONS.GrangerOrder = sProcess.options.grangerorder.Value{1};
    OPTIONS.pThresh      = 0.05;
    OPTIONS.Freqs        = 0;

    % Compute metric
    OutputFiles = bst_connectivity({sInputA.FileName}, [], OPTIONS);
end

%% ===== TEST FUNCTION =====
function Test() %#ok<DEFNU>
    % Start a new report
    bst_report('Start');
    % Get test datasets
    sFile = process_simulate_ar('Test');
    % Coherence process
    sTmp = bst_process('CallProcess', 'process_granger1n', sFile, [], ...
        'timewindow',   [], ...    % All the time in input
        'grangerorder', 10, ...
        'outputmode',   1);        % Save individual results (one file per input file)
    % Snapshot: spectrum
    bst_process('CallProcess', 'process_snapshot', sTmp, [], ...
        'target',       11, ...  % Connectivity matrix (image)
        'modality',     1, 'orient', 1, 'time', 0, 'contact_time', [-40, 110], 'contact_nimage', 16, ...
        'Comment',      [sFile.Comment, ': ' sTmp.Comment]);
    % Save and display report
    ReportFile = bst_report('Save', sFile);
    bst_report('Open', ReportFile);
end


function D = addsmallobjectlabel(D, height, width, method)
%
% D = addsmallobjectlabel(D, height, width)
%
% Add the 'smallobject' label to any object that both dimensions are
% smaller than 32 pixels:
%
% D = addsmallobjectlabel(D, 32, 32)
%
% You can specify two decisions:
% D = addsmallobjectlabel(D, 32, 32, method)
%
% method = [and] | or
%
%
% Then we can use the LMquery to remove all the small objects.

if nargin<4
    method = 'and';
end

for i = 1:length(D)
    if isfield(D(i).annotation, 'object')
        Nobjects = length(D(i).annotation.object);
        bb = LMobjectboundingbox(D(i).annotation, 1:Nobjects)';

        for j = 1:Nobjects
            H = bb(4,:)-bb(2,:); % height of each annotated object
            W = bb(3,:)-bb(1,:); % width of each annotated object

            switch method
                case 'and'
                    small = find((H<height)&(W<width));
                case 'or'
                    small = find((H<height)|(W<width));
                otherwise
                    error('Invalid method')
            end

            for n = small
                D(i).annotation.object(n).name = [strtrim(D(i).annotation.object(n).name) ' smallobject'];
            end
        end
    end
end


function sz = tsize(a,idx)
%TSIZE Tensor size of sptenmat.
%
%   D = TSIZE(X) returns the size of the tensor being stored as a
%   matrix.
%
%   M = TSIZE(X,DIM) returns the length of the dimension(s) specified
%   by DIM.  For example, SIZE(X,1) returns the size of the first
%   dimension of the tensor.
%
%   See also SPTENMAT, SPTENMAT/SIZE.
%
%MATLAB Tensor Toolbox.
%Copyright 2012, Sandia Corporation.

% This is the MATLAB Tensor Toolbox by T. Kolda, B. Bader, and others.
% http://www.sandia.gov/~tgkolda/TensorToolbox.
% Copyright (2012) Sandia Corporation. Under the terms of Contract
% DE-AC04-94AL85000, there is a non-exclusive license for use of this
% work by or on behalf of the U.S. Government. Export of this data may
% require a license from the United States Government.
% The full license terms can be found in the file LICENSE.txt

if isempty(a.tsize)
    sz = [];
    return;
end

if exist('idx', 'var')
    sz = a.tsize(idx);
else
    sz = a.tsize;
end

return;


function [ebsd, header] = loadEBSD_h5(fname,varargin)

if ~exist(fname,'file'), error(['File ' fname ' not found!']); end

ginfo = locFindEBSDGroups(fname);

if numel(ginfo) > 0

  groupNames = {ginfo.Name};

  patternMatch = false(size(groupNames));
  for k=1:numel(varargin)
    if ischar(varargin{k})
      patternMatch = patternMatch | strncmpi(groupNames,varargin{k},numel(varargin{k}));
    end
  end

  if nnz(patternMatch) == 0
    if length(ginfo) > 1
      [sel,ok] = listdlg('ListString',{ginfo.Name},'ListSize',[400 300]);

      if ok
        ginfo = ginfo(sel);
      else
        return
      end
    end
  else
    ginfo = ginfo(patternMatch);
  end
end

assert(numel(ginfo) > 0);

if check_option(varargin,'check'), ebsd = EBSD; header = {}; return; end

try
  for k = 1:numel(ginfo)
    kGroup = ginfo(k);

    CS = locReadh5Phase(fname,kGroup);

    kGroupData = kGroup.Groups(1).Datasets;

    props = struct;

    for j=1:numel(kGroupData)

      if length(kGroupData(j).ChunkSize) > 1, continue; end
      data = double(h5read(fname,[kGroup.Groups(1).Name '/' kGroupData(j).Name]));

      name = strrep(kGroupData(j).Name,' ','_');

      name = strrep(name,'X_Position','x');
      name = strrep(name,'Y_Position','y');
      name = strrep(name,'X_SAMPLE','x');
      name = strrep(name,'Y_SAMPLE','y');

      name = regexprep(name,'phi','Phi','ignorecase');
      name = regexprep(name,'phi1','phi1','ignorecase');
      name = regexprep(name,'phi2','phi2','ignorecase');

      props.(name) = data(:);
    end

    % sometimes not indexed orientations are marked as 4*pi
    notIndexed = isappr(props.phi1,4*pi,1e-5);
    if all(props.phi1(~notIndexed)<=2.001*pi) ...
        && all(props.Phi(~notIndexed)<=1.001*pi) ...
        && all(props.phi2(~notIndexed)<=2.001*pi)

      props.phi1(notIndexed) = NaN;
      props.phi2(notIndexed) = NaN;
      props.Phi(notIndexed) = NaN;

      isDegree = 1;

    else
      isDegree = degree;
    end

    rot = rotation.byEuler(props.phi1*isDegree,props.Phi*isDegree,props.phi2*isDegree);
    phases = props.Phase;

    props = rmfield(props,{'Phi','phi1','phi2','Phase'});

    ebsd = EBSD(rot,phases,CS,props);

    ind = props.x > -11111;
    ebsd = ebsd(ind);
    ebsd.unitCell = calcUnitCell([ebsd.prop.x,ebsd.prop.y]);

    if length(kGroup.Groups) > 1
      header = h5group2struct(fname,kGroup.Groups(2));
    else
      header = [];
    end

  end
end

end

function [ginfo] = locFindEBSDGroups(fname)

info = h5info(fname,'/');

ginfo = struct('Name',{},...
  'Groups',{},...
  'Datasets',{},...
  'Datatypes',{},...
  'Links',{},...
  'Attributes',{});

ginfo = locGr(fname, info.Groups,ginfo);
end

function [ginfo] = locGr(fname,group,ginfo)

if ~isempty(group)

  for k=1:numel(group)
    attr  = group(k).Attributes;
    name = group(k).Name;

    if (~isempty(attr) && check_option({attr.Value},'EBSD')) || strcmp(name(end-4:end),'/EBSD')

      ginfo(end+1) = group(k);

    end

    [ginfo] = locGr(fname,group(k).Groups,ginfo);
  end
end
end

function CS = locReadh5Phase(fname,group)
% load phase informations from h5 file

group = group.Groups(strcmp([group.Name '/Header'],{group.Groups.Name}));
token = [group.Name '/Phase'];
group = group.Groups(strncmp(token,{group.Groups.Name},length(token)));

for iphase = 1:numel(group.Groups)

  try
    mineral = strtrim(char(h5read(fname,[group.Groups(iphase).Name '/MaterialName'])));
  catch
    mineral = strtrim(char(h5read(fname,[group.Groups(iphase).Name '/Name'])));
  end
  formula = strtrim(char(h5read(fname,[group.Groups(iphase).Name '/Formula'])));

  try
    lattice(1) = h5read(fname,[group.Groups(iphase).Name '/Lattice Constant a']);
    lattice(2) = h5read(fname,[group.Groups(iphase).Name '/Lattice Constant b']);
    lattice(3) = h5read(fname,[group.Groups(iphase).Name '/Lattice Constant c']);

    lattice(4) = h5read(fname,[group.Groups(iphase).Name '/Lattice Constant alpha']);
    lattice(5) = h5read(fname,[group.Groups(iphase).Name '/Lattice Constant beta']);
    lattice(6) = h5read(fname,[group.Groups(iphase).Name '/Lattice Constant gamma']);

    pointGroup = h5read(fname,[group.Groups(iphase).Name '/Point Group']);
    pointGroup = regexp(char(pointGroup),'\[([1-6m/\-]*)\]','tokens');
    spaceGroup = pointGroup{1};

  catch
    lattice = h5read(fname,[group.Groups(iphase).Name '/LatticeConstants']);

    spaceGroup = h5read(fname,[group.Groups(iphase).Name '/SpaceGroup']);
    spaceGroup = strrep(spaceGroup,'#ovl','-');
    spaceGroup = strrep(spaceGroup,'#sub','');
  end

  try
    CS{iphase} = crystalSymmetry(spaceGroup,double(lattice(1:3)),double(lattice(4:6))*degree,'mineral',mineral);
  catch
    CS{iphase} = 'notIndexed';
  end
end

end


function [t,options] = loadTensor_json(fname,varargin)

[~,~,ext] = fileparts(fname);
if ~strcmpi(ext,'.json'), interfaceError(fname), end

s = loadjson(fname);

type = char(fieldnames(s));

s = s.(type);

cs = crystalSymmetry(s.CS.pointGroup,s.CS.abc,s.CS.abg,s.CS.alignment{:},'mineral',s.CS.mineral);

t = feval(type,s.M,cs,'rank',s.rank);
try t.opt = s.opt; end

options = {};


function [GraphObj]=createMetIntrcNetwork(model,metAbbr,varargin)
% Create directed metabolite-metabolite interaction network using given metabolites and the model.
% The produced network consists of given metabolites and its first neighbours.
% Colour and width of edges will be adjusted based on flux values If fluxes of the model are given.
% Another metabolite-metabolite interaction network will be generated if the node is clicked;
%
% Left click :Generate sub-metabolite-metabolite interaction network from the created figure.
%            This functionality was added for better looking at the created network
%            and showing flux values on edges lines.
%
% Right click :Generate metabolite-metabolite interaction network from model. This
%             functionality was added for creating metabolite-metabolite network using
%             clicked metabolite and model.
% The produced figure by right and left click on the main figure also
% has same property with the main figure, and it is clickable too.
%
% Note : If the fluxes are not given, irreversible COBRA model structure should use for
%        representing reversible reactions. See convertToIrreversible function at github page of cobratoolbox
%        https://opencobra.github.io/cobratoolbox/stable/modules/reconstruction/refinement/
% USAGE:
%
%    [GraphObj]=createMetIntrcNetwork(model,metAbbr,varargin)
%
% INPUTS:
%
%    model:             COBRA model structure
%    metAbbr:           List of metabolite abbreviation as a cell array
%
% OPTIONAL INPUT:
%    varargin:      Optional Inputs provided as 'ParameterName', Value
%                   pairs. the following parameternames are available:
%
%                   * fluxes:             flux vector
%                   * Graphtitle:         Title for a figure as a string
%                   * excludedMets:       As a cell array, Metabolite abbreviations that desired for exclude
%                   * nodeColour:         Colour for nodes (RGB Triplet) e.g. [0 1 0]
%                                         see https://www.mathworks.com/help/matlab/ref/colorspec.html
%                   * Hnodecolour:         Colour for nodes that will be highlighted e.g. [0 1 1]
%                   * scaleMin            Minimum value for scaling colorbar
%                   * scaleMax            Maximum value for scaling colorbar
%                   * nodeSize            Size of Nodes
%                   * HnodeSize           Highlighted node size
%                   * arrowSize           Arrow Size
%                   * threshold           Treshold for edges, the edges that has the flux value
%                                         below the treshold will be deleted, and if the node don't
%                                         have any relationship with any other nodes will be deleted.
%                   * excNodesWithDeg     It deletes nodes that has not degree in a given range.
%                                         It should be given as [minimum degree maximum degree].
%                                         Example, [1 50]. it deletes nodes that has lower than 1 degree
%                                         and higher than 50 degree.
%
% OUTPUT:
%    GraphObj:              Matlab digraph object
%
%
% EXAMPLES:
%    1) create network with succ[c] and akg[c]
%       [GraphObj]=createMetIntrcNetwork(model,{'succ[c]','akg[c]'})
%    2) create network with flux values
%       [GraphObj]=createMetIntrcNetwork(model,{'succ[c]','akg[c]'},'fluxes',fluxvector)
%    3) create network with flux values, and exculude exclude very employed metabolites
%       [GraphObj]=createMetIntrcNetwork(model,{'succ[c]','akg[c]'},'fluxes',fluxvector,'excludedMets',{'atp[c]','adp[c]'})
%    4) create network with scaling colorbar, [0 1]
%       [GraphObj]=createMetIntrcNetwork(model,{'succ[c]','akg[c]'},'fluxes',fluxvector,'excludedMets',{'atp[c]','adp[c]'},'scaleMax',1,'scaleMin',0)
%
% .. Author: - Kadir KOCABAS November/2020

defaultGraphtitle ='Metabolite-Metabolite Interaction Network';
defaultexcludedMets ={};
defaultFluxes=ones(size(model.rxns,1),1);
defaultnodeColour=[0.9 0.7 1];
defaultHnodecolour=[0 1 0];
defaultscaleMax=1e-6;
defaultscaleMin=0;
defaultNodeSize=15;
defaultHNodeSize=25;
defaultarrowSize=9;
defaultThreshold=1e-7;
defaultexcNodesWithDeg=[0 1e+6];

p = inputParser;
addRequired(p,'model',@isstruct);
addRequired(p,'metAbbr',@iscell);
addParameter(p,'fluxes',defaultFluxes,@isvector);
addParameter(p,'Graphtitle',defaultGraphtitle,@(x) isstring(x) || ischar(x));
addParameter(p,'excludedMets',defaultexcludedMets,@iscell);
addParameter(p,'nodeColour',defaultnodeColour,@isvector);
addParameter(p,'Hnodecolour',defaultHnodecolour,@isvector);
addParameter(p,'nodeSize',defaultNodeSize,@isnumeric);
addParameter(p,'HnodeSize',defaultHNodeSize,@isnumeric);
addParameter(p,'arrowSize',defaultarrowSize,@isnumeric);
addParameter(p,'scaleMax',defaultscaleMax,@isnumeric);
addParameter(p,'scaleMin',defaultscaleMin,@isnumeric);
addParameter(p,'threshold',defaultThreshold,@isnumeric);
addParameter(p,'excNodesWithDeg',defaultexcNodesWithDeg,@isvector);

parse(p,model,metAbbr,varargin{:});
p.KeepUnmatched = true;
model=p.Results.model;
metAbbr=p.Results.metAbbr;
fluxes=p.Results.fluxes;
Graphtitle=p.Results.Graphtitle;
excludedMets=p.Results.excludedMets;
nodeColour=p.Results.nodeColour;
Hnodecolour=p.Results.Hnodecolour;
scaleMin=p.Results.scaleMin;
scaleMax=p.Results.scaleMax;
nodeSize=p.Results.nodeSize;
HnodeSize=p.Results.HnodeSize;
arrowSize=p.Results.arrowSize;
threshold=p.Results.threshold;
excNodesWithDeg=p.Results.excNodesWithDeg;

%Controlling scale values
if ~(scaleMax>scaleMin)
    error('scaleMax should bigger than scaleMin');
end

%controlling degre values
if (excNodesWithDeg(1)>excNodesWithDeg(2))
    error('maximum degree should bigger or equal to minimum degree');
end

%controlling if flux vakues given
if (sum(fluxes)==size(model.rxns,1)&all(ismember(fluxes,1)))
    fcont=1;

else
    fcont=0;
end

if ((fcont==1) & ~(scaleMax==1e-6 && scaleMin==0) )
    warning('Scales are given without giving fluxes')
end

if ~all(ismember(metAbbr,model.mets))
    str = strjoin( metAbbr(~ismember(metAbbr,model.mets)), ' ' );
    warning(['These metabolites are not found in model : ' str])
end

%if user wants to delete edges with 0 fluxes treeshold assign 1e-5. Because
%weight of edges with zero fluxes shown as 1e-6 since weight of edges cannot
%be 0 in the matlab graph objects

if threshold==0
    threshold=1e-5;
end

%Create table, creating table without fluxrates if fluxes are not supplied
IndexesofMets=find(ismember(model.mets,metAbbr));
metMatrix=~ismember(model.S(IndexesofMets,:),0);
Rxns=model.rxns(any(metMatrix,1));
FluxRes=fluxes(ismember(model.rxns,Rxns));
%
%Get sub S matrix from S matrix
RxnsMatrx=~ismember(model.S(:,any(metMatrix,1)),0);
InvolvedMets=model.mets(any(RxnsMatrx,2));
adjunc_mat=model.S(any(RxnsMatrx,2),any(metMatrix,1));
%
%Multiply sub S matrix with flux values to keep flux value information
FluxMatrix  =FluxRes+1e-6;
FluxMatrix = repmat(FluxMatrix',size(adjunc_mat,1),1);
adjunc_mat=adjunc_mat.*FluxMatrix;

%Create left and right matrix( left matrix : comsumed metabolites, righ matrix:
%produced metabolites)
leftMatrix=adjunc_mat;
leftMatrix(leftMatrix > 0)=0;
rightMatrix=adjunc_mat;
rightMatrix(rightMatrix < 0)=0;
rightMatrix(rightMatrix > 0)=1;

%create adjacency matrix
A=(leftMatrix*rightMatrix')*(-1);

%Delete metabolites that is not first neighbour of metAbbr
idx=ismember(InvolvedMets,metAbbr);
newadjMtrx=zeros(size(A));
newadjMtrx(idx,:)=A(idx,:);
newadjMtrx(:,idx)=A(:,idx);
rows=any(newadjMtrx,1);
cols=any(newadjMtrx,2);
indofMets=rows'|cols;
metnames=InvolvedMets(indofMets);
newadjMtrx=newadjMtrx(indofMets,indofMets);
A=newadjMtrx;

%create graph object.
G = digraph(A,metnames);

GraphObj=G;
excludedMets=excludedMets(~ismember(excludedMets,metAbbr));

%Exclude given metabolites
G=rmnode(G,excludedMets);

if threshold~=1e-7
    edesBelowTresholdIdx=find(G.Edges.Weight<threshold);
    G=rmedge(G,edesBelowTresholdIdx);
end

%controlling if there is a node with given range
nodesIndegree=indegree(G);
nodesoutdegree=outdegree(G);
totalDegree=nodesIndegree+nodesoutdegree;
minDegree=excNodesWithDeg(1);
maxDegree=excNodesWithDeg(2);

if (max(totalDegree) < minDegree | min(totalDegree) > maxDegree)
    error('There is no node with degree in the given range')
end

G=rmnode(G,find(totalDegree<minDegree | totalDegree>maxDegree ));

figure;
hold on

%chech if there is a edge in the network
if ~isempty(G.Edges.Weight)
    G.Edges.LWidths = (G.Edges.Weight-min(G.Edges.Weight))/(max(G.Edges.Weight)-min(G.Edges.Weight))+0.00001;
    edgeCont=true;
else
    warning('There is no edges in the Network');
    edgeCont=false;
end

%create plot
h=plot(G,'MarkerSize',nodeSize,'NodeColor',nodeColour,'ArrowSize',arrowSize,'NodeLabelMode','auto','Layout','force','UseGravity',true);
metAbbr=metAbbr(ismember(metAbbr,G.Nodes.Name));
highlight(h,metAbbr,'NodeColor',Hnodecolour,'MarkerSize',HnodeSize);

nl = h.NodeLabel;
h.NodeLabel = '';
xd = get(h, 'XData');
yd = get(h, 'YData');
title(Graphtitle,'Interpreter', 'none');
txt=text(xd, yd, nl, 'FontSize',8, 'FontWeight','bold', 'HorizontalAlignment','center', 'VerticalAlignment','middle');
set(txt,'Interpreter', 'none');
%Define function for texts
set(txt,'ButtonDownFcn',{@hit_node,A,metnames,model,fluxes,excludedMets,Graphtitle,nodeColour,Hnodecolour,fcont,scaleMin,scaleMax,nodeSize,HnodeSize,arrowSize,threshold,excNodesWithDeg});

%define color and width of edges if fluxes are given
if edgeCont % control if there is edge in the network
    if  fcont==1
        h.LineWidth=1;
    elseif ~any(isnan(G.Edges.LWidths))
        h.LineWidth=G.Edges.LWidths*2.5;
        colormap jet(10)
        h.EdgeCData=G.Edges.Weight;
        hcb=colorbar;
        colorTitleHandle = get(hcb,'Title');
    else
        h.LineWidth=1;
        colormap jet(10)
        h.EdgeCData=G.Edges.Weight;
        hcb=colorbar;
        colorTitleHandle = get(hcb,'Title');

    end

    if fcont==0
        if ~(scaleMax==1e-6 && scaleMin==0)
            caxis([scaleMin scaleMax])
            titleBar = ['Scaled Fluxes',' ', '(', num2str(scaleMin),' - ',num2str(scaleMax),')'];
            set(colorTitleHandle ,'String',titleBar,'FontWeight','Bold');
        else
            set(colorTitleHandle ,'String','Fluxes','FontWeight','Bold');
        end
    end
end

set(gca,'XTickLabel',{' '});
set(gca,'YTickLabel',{' '});
set(gca,'YTick',[]);
set(gca,'XTick',[]);
set(gca,'XColor', 'none','YColor','none');
end


%% rrt_star_fn
% *RRT*FN* is a memory efficient variant of RRT*. It inherents all
% the optimization features of RRT*, but in contrast using limited
% number of nodes i.e. memory is limited.
%
%% Syntax
%  problem = rrt_star(map, max_iter, max_nodes, is_benchmark, rand_seed, variant)
%  function returns the object of the respective class with the result
%
%% Input
%  map           -- struct with appropriate fields (developer of
%                the class provides more information on this topic)
%  max_iter      -- number of iteration to solve the problem
%  max_nodes     -- the maximum allowed number of nodes added to the tree
%  is_benchmark  -- if true saves snapshots of the tree in a special directory
%                boolean variable
%  rand_seed     -- a random seed
%  variant       -- what class to choose, class used defines the problem space
%% Output
% *problem* is an object of an appropriate problem space representing class
%


% Copyright (C) 1993-2014, by Peter I. Corke
%
% This file is part of The Robotics Toolbox for Matlab (RTB).
%
% RTB 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.
%
% RTB 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 Leser General Public License
% along with RTB.  If not, see <http://www.gnu.org/licenses/>.
function [sys,x0,str,ts] = quadrotor_plot(t,x,u,flag,s,plot,enable,vehicle)
    % Flyer plot, lovingly coded by Paul Pounds, first coded 17/4/02
    % version 2 2004 added scaling and ground display
    % version 3 2010 improved rotor rendering and fixed mirroring bug
    %
    % Displays X-4 flyer position and attitude in a 3D plot.
    % GREEN ROTOR POINTS NORTH
    % BLUE ROTOR POINTS EAST

    % PARAMETERS
    % s defines the plot size in meters
    % swi controls flyer attitude plot; 1 = on, otherwise off.

    % INPUTS
    % 1 Center X position
    % 2 Center Y position
    % 3 Center Z position
    % 4 Yaw angle in rad
    % 5 Pitch angle in rad
    % 6 Roll angle in rad

    % OUTPUTS
    %   None
    ts = [-1 0];

    if ~isfield(vehicle, 'nrotors')
        vehicle.nrotors = 4;    % sensible default for quadrotor function
    end

    switch flag,
        case 0
            [sys,x0,str,ts] = mdlInitializeSizes(ts,plot,enable); % Initialization
        case 3
            sys = mdlOutputs(t,u,s,plot,enable, vehicle); % Calculate outputs
        case {1,2, 4, 9} % Unused flags
            sys = [];
        otherwise
            error(['unhandled flag = ',num2str(flag)]); % Error handling
    end

    % Initialize
function [sys,x0,str,ts] = mdlInitializeSizes(ts,plot,enable)
    % Call simsizes for a sizes structure, fill it in, and convert it
    % to a sizes array.
    sizes = simsizes;
    sizes.NumContStates  = 0;
    sizes.NumDiscStates  = 0;
    sizes.NumOutputs     = 0;
    sizes.NumInputs      = 6;
    sizes.DirFeedthrough = 1;
    sizes.NumSampleTimes = 1;
    sys = simsizes(sizes);
    x0  = [];
    str = [];          % Set str to an empty matrix.
    ts = [0.05 0];

    if enable == 1
        figure(plot);
        clf;
        %colordef(1,'none');
    end
    % End of mdlInitializeSizes.

function sys = mdlOutputs(t,u,s, plot, enable, quad)
    global a1s b1s

    % not quite sure what this is about -- PIC
    if numel(a1s) == [0];
        a1s = zeros(1, quad.nrotors);
        b1s = zeros(1, quad.nrotors);
    end

    % vehicle dimensons
    d = quad.d; %Hub displacement from COG
    r = quad.r; %Rotor radius

    for i = 1:quad.nrotors
        theta = (i-1)/quad.nrotors*2*pi;
        %   Di      Rotor hub displacements (1x3)
        % first rotor is on the x-axis, clockwise order looking down from above
        D(:,i) = [ d*cos(theta); d*sin(theta); 0];
        scal = s(1)/4;
        %Attitude center displacements
        C(:,i) = [ scal*cos(theta); scal*sin(theta); 0];
    end

    if enable == 1
        %draw ground
        figure(plot);
        clf;
        if length(s) == 1
            axis([-s s -s s 0 s]);
        else
            axis([-s(1) s(1) -s(1) s(1) 0 s(2)])
            s = s(1);
        end
        hold on;

        % plot the ground boundaries and the big cross
        plot3([-s -s],[s -s],[0 0],'-b')
        plot3([-s s],[s s],[0 0],'-b')
        plot3([s -s],[-s -s],[0 0],'-b')
        plot3([s s],[s -s],[0 0],'-b')
        plot3([s -s],[-s s],[0 0],'-b')
        plot3([-s s],[-s s],[0 0],'-b')

        %READ STATE
        z = [u(1);u(2);u(3)];
        n = [u(4);u(5);u(6)];

        %PREPROCESS ROTATION MATRIX
        phi = n(1);    %Euler angles
        the = n(2);
        psi = n(3);

        R = [cos(the)*cos(phi) sin(psi)*sin(the)*cos(phi)-cos(psi)*sin(phi) cos(psi)*sin(the)*cos(phi)+sin(psi)*sin(phi);   %BBF > Inertial rotation matrix
            cos(the)*sin(phi) sin(psi)*sin(the)*sin(phi)+cos(psi)*cos(phi) cos(psi)*sin(the)*sin(phi)-sin(psi)*cos(phi);
            -sin(the)         sin(psi)*cos(the)                            cos(psi)*cos(the)];

        %Manual Construction
        %Q3 = [cos(psi) -sin(psi) 0;sin(psi) cos(psi) 0;0 0 1];   %Rotation mappings
        %Q2 = [cos(the) 0 sin(the);0 1 0;-sin(the) 0 cos(the)];
        %Q1 = [1 0 0;0 cos(phi) -sin(phi);0 sin(phi) cos(phi)];
        %R = Q3*Q2*Q1;    %Rotation matrix

        %CALCULATE FLYER TIP POSITONS USING COORDINATE FRAME ROTATION
        F = [1 0 0;0 -1 0;0 0 -1];

        %Draw flyer rotors
        t = [0:pi/8:2*pi];
        for j = 1:length(t)
            circle(:,j) = [r*sin(t(j));r*cos(t(j));0];
        end

        for i = 1:quad.nrotors
            hub(:,i) = F*(z + R*D(:,i)); %points in the inertial frame

            q = 1; %Flapping angle scaling for output display - makes it easier to see what flapping is occurring
            Rr = [cos(q*a1s(i))  sin(q*b1s(i))*sin(q*a1s(i)) cos(q*b1s(i))*sin(q*a1s(i));   %Rotor > Plot frame
                0              cos(q*b1s(i))               -sin(q*b1s(i));
                -sin(q*a1s(i)) sin(q*b1s(i))*cos(q*a1s(i)) cos(q*b1s(i))*cos(q*a1s(i))];

            tippath(:,:,i) = F*R*Rr*circle;
            plot3([hub(1,i)+tippath(1,:,i)],[hub(2,i)+tippath(2,:,i)],[hub(3,i)+tippath(3,:,i)],'b-')
        end

        %Draw flyer
        hub0 = F*z;  % centre of vehicle
        for i = 1:quad.nrotors
            % line from hub to centre plot3([hub(1,N) hub(1,S)],[hub(2,N) hub(2,S)],[hub(3,N) hub(3,S)],'-b')
            plot3([hub(1,i) hub0(1)],[hub(2,i) hub0(2)],[hub(3,i) hub0(3)],'-b')

            % plot a circle at the hub itself
            plot3([hub(1,i)],[hub(2,i)],[hub(3,i)],'o')
        end

        % plot the vehicle's centroid on the ground plane
        plot3([z(1) 0],[-z(2) 0],[0 0],'--k')
        plot3([z(1)],[-z(2)],[0],'xk')

        % label the axes
        xlabel('x');
        ylabel('y');
        zlabel('z (height above ground)');
    end

    sys = [];
    % End of mdlOutputs.


function test_bug2570

% WALLTIME 00:10:00
% MEM 1gb
% DEPENDENCY ft_apply_montage ft_scalingfactor

montage = [];
montage.tra = 1e6;
montage.labelold = {'Cz'};
montage.labelnew = {'Cz'};
montage.chanunitold = {'V'};
montage.chanunitnew = {'uV'};

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

output = ft_apply_montage(montage, montage);
assert(isequal(output, montage)); % it should not have changed

output = ft_apply_montage(montage, montage, 'inverse', 1);
assert(isequal(output.tra, 1)); % this should be an identity transform
assert(isequal(output.chanunitold, output.chanunitnew));

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

data_V = [];
data_V.label = {'Cz'};
data_V.time = {1:10};
data_V.trial = {ones(1,10)};
data_V.chanunit = {'V'};

data_mV = [];
data_mV.label = {'Cz'};
data_mV.time = {1:10};
data_mV.trial = {ones(1,10)*1000};
data_mV.chanunit = {'mV'};

data_uV = [];
data_uV.label = {'Cz'};
data_uV.time = {1:10};
data_uV.trial = {ones(1,10)*1000000};
data_uV.chanunit = {'uV'};

output1 = ft_apply_montage(data_V,  montage);
output2 = ft_apply_montage(data_mV, montage);
output3 = ft_apply_montage(data_uV, montage);

assert(isequal(output1, output2));
assert(isequal(output1, output3));
assert(isequal(output1, data_uV)); % they should all be in uV

output1 = ft_apply_montage(data_V,  montage, 'inverse', 'yes');
output2 = ft_apply_montage(data_mV, montage, 'inverse', 'yes');
output3 = ft_apply_montage(data_uV, montage, 'inverse', 'yes');

assert(isequal(output1, output2));
assert(isequal(output1, output3));
assert(isequal(output1, data_V)); % they should all be in V

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

data_T = [];
data_T.label = {'Cz'};
data_T.time = {1:10};
data_T.trial = {ones(1,10)};
data_T.chanunit = {'T'};

try
  ft_apply_montage(data_T,  montage);
  ok = false;
catch
  ok = true;
end
if ~ok
  error('this should have given an error, T cannot be converted to V');
end


function test_suite = test_chunkize
% tests for cosmo_chunkize
%
% #   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 test_chunkize_basis
    ds=cosmo_synthetic_dataset('type','timelock','nreps',8);
    ds.sa.chunks=reshape(repmat((1:8),6,1),[],1);
    ds=cosmo_slice(ds,randperm(48));

    chunks=cosmo_chunkize(ds,8);
    assertEqual(chunks,ds.sa.chunks);

    for j=1:2:7
        chunks=cosmo_chunkize(ds,j);
        eq_chunks=bsxfun(@eq,chunks,chunks');
        eq_ds=bsxfun(@eq,ds.sa.chunks,ds.sa.chunks');

        m=eq_ds & ~eq_chunks;
        assert(~any(m(:)));
    end

    assertExceptionThrown(@()cosmo_chunkize(ds,9),'');
    ds=rmfield(ds.sa,'chunks');
    assertExceptionThrown(@()cosmo_chunkize(ds,2),'');

function test_chunkize_imbalance()
    ds=struct();
    ds.samples=(1:5)';
    assertExceptionThrown(@()cosmo_chunkize(ds,2),'');
    ds.sa.chunks=2+[1 1 2 2 2]';
    assertExceptionThrown(@()cosmo_chunkize(ds,2),'');
    ds.sa.targets=10+[1 2 1 2 2]';
    assertExceptionThrown(@()cosmo_chunkize(ds,3),'');

    count=2;
    res=cosmo_chunkize(ds,count);
    assert_chunkize_ok(ds,res,count);

    ds2=cosmo_stack({ds,ds});
    res2=cosmo_chunkize(ds2,count);
    assert_chunkize_ok(ds2,res2,count);

function test_all_unique_chunks_tiny()
    ds=struct();
    ds.samples=(1:5)';
    ds.sa.targets=2+[1 1 2 2 2]';
    ds.sa.chunks=10+[1 2 3 4 5]';

    for count=2:5
        res=cosmo_chunkize(ds,count);
        assert_chunkize_ok(ds,res,count);
    end

    assertExceptionThrown(@()cosmo_chunkize(ds,6),'');

function test_chunkize_very_unbalanced_chunks_big()
    % all chunks are unique, want a similar number of targets in each
    % output chunk
    ds=cosmo_synthetic_dataset('nreps',6,'ntargets',5);

    nsamples=size(ds.samples,1);
    ds.sa.chunks(:)=repmat((1:nsamples/10),1,10);

    n_combis=max(ds.sa.chunks)*max(ds.sa.targets);

    targets=ds.sa.targets;
    n_swap=5;

    while true
        rp=randperm(nsamples);
        ds.sa.targets=targets;
        ds.sa.targets(rp(1:n_swap))=ds.sa.targets(rp(n_swap:-1:1));
        idxs=cosmo_index_unique({ds.sa.targets,ds.sa.chunks});
        n=cellfun(@numel,idxs);
        if min(n)>=1 && max(n)<=3 && std(n)<.1 && numel(n)==n_combis
            % not too unbalanced
            break;
        end
    end

    nchunks=ceil(3+rand()*4);
    res=cosmo_chunkize(ds,nchunks);
    assert_chunkize_ok(ds,res,nchunks);

function test_chunkize_slight_unbalanced_chunks_big()
    % all chunks are unique, want a similar number of targets in each
    % output chunk
    ds=cosmo_synthetic_dataset('nreps',6,'ntargets',5);

    nsamples=size(ds.samples,1);
    ds.sa.chunks(:)=repmat((1:nsamples/2),1,2);
    ds.sa.targets(1:5)=ds.sa.targets(2:6); % slight imbalance

    nchunks=ceil(rand()*5);
    res=cosmo_chunkize(ds,nchunks);
    assert_chunkize_ok(ds,res,nchunks);

function test_chunkize_all_unique_independent_chunks()
% each sample has its own unique chunk value
    ds=cosmo_synthetic_dataset('ntargets',2,'nchunks',6*6);
    nsamples=size(ds.samples,1);
    ds.sa.chunks(:)=ceil(rand()*10)+(1:nsamples);
    ds.sa.targets=ds.sa.targets(randperm(nsamples));

    nchunks_candidates=[1 2 3 4 6 12 18];
    for nchunks=nchunks_candidates
        chunks=cosmo_chunkize(ds,nchunks);
        assert_chunkize_ok(ds,chunks,nchunks);

        idxs=cosmo_index_unique([ds.sa.targets chunks]);
        n=cellfun(@numel,idxs);

        % require full balance
        assert(all(n(1)==n(2:end)));
    end

function test_chunkize_dependent_balanced_chunks()
% each combination of chunks and targets occurs equally often
    ntargets=ceil(2+rand()*4);
    nreps=ceil(2+rand()*4);
    nchunks=36;
    ds=cosmo_synthetic_dataset('ntargets',ntargets,...
                                'nchunks',nchunks,'nreps',nreps);
    nsamples=size(ds.samples,1);
    ds=cosmo_slice(ds,randperm(nsamples));

    rep_idxs=cosmo_index_unique({ds.sa.chunks,ds.sa.targets});
    assert(all(cellfun(@numel,rep_idxs)==nreps));

    nchunks_candidates=[1 2 3 4 6 12 18];
    for nchunks=nchunks_candidates
        chunks=cosmo_chunkize(ds,nchunks);
        assert_chunkize_ok(ds,chunks,nchunks);

        idxs=cosmo_index_unique([ds.sa.targets chunks]);
        n=cellfun(@numel,idxs);

        % require full balance
        assert(all(n(1)==n(2:end)));
    end

function assert_chunkize_ok(src_ds,chunks,count)
    % number of items must match input dataset
    assertEqual(numel(src_ds.sa.chunks),numel(chunks));

    % must be balanced
    assert_chunks_targets_balanced(src_ds,chunks);

    % cannot have double dipping
    assert_no_double_dipping(src_ds,chunks);

    % must have the proper number of chunks
    assertEqual(numel(unique(chunks)),count);

    assert_chunks_targets_nonzero(src_ds,chunks);

function assert_chunks_targets_balanced(src_ds,chunks)
    idxs=cosmo_index_unique([src_ds.sa.targets chunks]);

    n=cellfun(@numel,idxs);

    % cannot test for 'optimal' balance due to combinatorial explosion;
    % this is a decent approach to make sure that chunks are not too
    % imbalanced
    assert(std(n)<=1.5);
    assert(min(n)+2>=max(n));

function assert_chunks_targets_nonzero(src_ds,chunks)
    [unused,unused,t_idxs]=unique(src_ds.sa.targets);
    [unused,unused,c_idxs]=unique(chunks);

    nt=max(t_idxs);
    nc=max(c_idxs);
    h=zeros(nt,nc);

    ns=numel(chunks);
    for k=1:ns
        t=t_idxs(k);
        c=c_idxs(k);
        h(t,c)=h(t,c)+1;
    end

    assert(all(max(h,[],1)>0));
    assert(all(max(h,[],2)>0));

function assert_no_double_dipping(src_ds,chunks)
    % samples that were in different chunks in src_ds must not be in the
    % same chunk in trg_ds
    [unq_src,unused,src_ids]=unique(src_ds.sa.chunks);
    [unq_trg,unused,trg_ids]=unique(chunks);

    n_src=numel(unq_src);
    n_trg=numel(unq_trg);
    n_samples=numel(src_ds.sa.chunks);
    chunk_count=zeros(n_src,n_trg);

    for k=1:n_samples
        i=src_ids(k);
        j=trg_ids(k);
        chunk_count(i,j)=chunk_count(i,j)+1;
    end

    assert(all(sum(chunk_count>0,2)==1));


function [ ] = display_graph(x_category, y_category, algorithm_list, w_list, info_list)
% SHow graphs of optimizations
%
% Inputs:
%       x_category          "numofgrad" or "iter" or "epoch" or "grad_calc_count"
%       y_category          "cost" or "optimality_gap" or "gnorm"
%       algorithms_list     algorithms to be evaluated
%       w_list              solution produced by each algorithm
%       info_list           statistics produced by each algorithm
%
%
% This file is part of GDLibrary and SGDLibrary.
%
% Created by H.Kasai on Oct. 23, 2016
% Modified by H.Kasai on Nov. 02, 2016

    % for plotting
    linetype = {'r','b','c','g','m','y','r--','b--','c--','g--','m--','y--','r:','b:','c:','g:','m:','y:','r.','b.','c.','g.','m.','y.'};
    fontsize = 16;
    markersize = 5;
    linewidth = 2;

    % initialize
    legend_str = cell(1);
    alg_num = 0;

    % plot
    figure;
    for alg_idx=1:length(algorithm_list)
        if ~isempty(info_list{alg_idx})
            alg_num = alg_num + 1;

            if strcmp(x_category, 'numofgrad')
                x_plot_data = info_list{alg_idx}.grad_calc_count;
            elseif strcmp(x_category, 'iter')
                x_plot_data = info_list{alg_idx}.iter;
            elseif strcmp(x_category, 'epoch')
                x_plot_data = info_list{alg_idx}.epoch;
            elseif strcmp(x_category, 'grad_calc_count')
                x_plot_data = info_list{alg_idx}.grad_calc_count;
            elseif strcmp(x_category, 'time')
                x_plot_data = info_list{alg_idx}.time;
            else
            end

            if strcmp(y_category, 'cost')
                y_plot_data = info_list{alg_idx}.cost;
            elseif strcmp(y_category, 'optimality_gap')
                y_plot_data = info_list{alg_idx}.optgap;
            elseif strcmp(y_category, 'gnorm')
                y_plot_data = info_list{alg_idx}.gnorm;
            elseif strcmp(y_category, 'K')
                y_plot_data = info_list{alg_idx}.K;
            elseif strcmp(y_category, 'orth')
                y_plot_data = info_list{alg_idx}.orth;
            elseif strcmp(y_category, 'symmetry')
                y_plot_data = [info_list{alg_idx}.symmetry];
            elseif strcmp(y_category, 'clustering_acc')
                y_plot_data = [info_list{alg_idx}.clustering_acc.acc];
            elseif strcmp(y_category, 'clustering_nmi')
                y_plot_data = [info_list{alg_idx}.clustering_acc.nmi];
            elseif strcmp(y_category, 'clustering_purity')
                y_plot_data = [info_list{alg_idx}.clustering_acc.purity];
            end

            semilogy(x_plot_data, y_plot_data, linetype{alg_num}, 'MarkerSize', markersize, 'Linewidth', linewidth); hold on;
            %plot(x_plot_data, y_plot_data, linetype{alg_num}, 'MarkerSize', markersize, 'Linewidth', linewidth); hold on;

            legend_str{alg_num} = algorithm_list{alg_idx};
        else
            %
        end
    end
    hold off;

    % X label
    if strcmp(x_category, 'numofgrad')
        xlabel('Number of gradient evaluations', 'FontSize', fontsize);
    elseif strcmp(x_category, 'iter')
        xlabel('Iteration', 'FontSize', fontsize);
    elseif strcmp(x_category, 'epoch')
        xlabel('Epoch', 'FontSize', fontsize);
    elseif strcmp(x_category, 'grad_calc_count')
        xlabel('# of grad', 'FontSize', fontsize);
    elseif strcmp(x_category, 'time')
        xlabel('Time', 'FontSize', fontsize);
    end

    % Y label
    if strcmp(y_category, 'cost')
        ylabel('Cost', 'FontSize', fontsize);
    elseif strcmp(y_category, 'optimality_gap')
        ylabel('Optimality gap', 'FontSize', fontsize);
    elseif strcmp(y_category, 'gnorm')
        ylabel('Norm of gradient', 'FontSize', fontsize);
    elseif strcmp(y_category, 'K')
        ylabel('Batch size', 'FontSize', fontsize);
    elseif strcmp(y_category, 'orth')
        ylabel('Orthogonality', 'FontSize', fontsize);
    elseif strcmp(y_category, 'symmetry')
        ylabel('norm(W-Wt)', 'FontSize', fontsize);
    elseif strcmp(y_category, 'clustering_acc')
        ylabel('ACC', 'FontSize', fontsize);
    elseif strcmp(y_category, 'clustering_nmi')
        ylabel('NMI', 'FontSize', fontsize);
    elseif strcmp(y_category, 'clustering_purity')
        ylabel('Purity', 'FontSize', fontsize);
    end
    legend(legend_str);
    set(gca, 'FontSize', fontsize);
end