function [ vertex_coordinate, vertex_label, edge_vertex, edge_label, ...
  triangle_vertex, triangle_label, quadrilateral_vertex, ...
  quadrilateral_label, tetrahedron_vertex, tetrahedron_label, ...
  hexahedron_vertex, hexahedron_label ] = mesh_data_read ( filename, dim, ...
  vertices, edges, triangles, quadrilaterals, tetrahedrons, hexahedrons )

%*****************************************************************************80
%
%% MESH_READ reads a MESH file defining a mesh and returns the data.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    08 November 2014
%
%  Author:
%
%    John Burkardt
%
%  Reference:
%
%    Pascal Frey,
%    MEDIT: An interactive mesh visualization software,
%    Technical Report RT-0253,
%    Institut National de Recherche en Informatique et en Automatique,
%    03 December 2001.
%
%  Parameters:
%
%    Input, string FILENAME, the name of the MESH file.
%
%    Input, integer DIM, the spatial dimension, which should be 2 or 3.
%
%    Input, integer VERTICES, the number of vertices.
%
%    Input, integer EDGES, the number of edges (may be 0).
%
%    Input, integer TRIANGLES, the number of triangles (may be 0).
%
%    Input, integer QUADRILATERALS, the number of quadrilaterals (may be 0).
%
%    Input, integer TETRAHEDRONS, the number of tetrahedrons (may be 0).
%
%    Input, integer HEXAHEDRONS, the number of hexahedrons (may be 0).
%
%    Output, real VERTEX_COORDINATE(DIM,VERTICES), the coordinates
%    of each vertex.
%
%    Output, integer VERTEX_LABEL(VERTICES), a label for each vertex.
%
%    Output, integer EDGE_VERTEX(2,EDGES), the vertices that form each edge.
%
%    Output, integer EDGE_LABEL(EDGES), a label for each edge.
%
%    Output, integer TRIANGLE_VERTEX(3,TRIANGLES), the vertices that form
%    each triangle.
%
%    Output, integer TRIANGLE_LABEL(TRIANGLES), a label for each triangle.
%
%    Output, integer QUADRILATERAL_VERTEX(4,QUADRILATERALS), the vertices that
%    form each quadrilateral.
%
%    Output, integer QUADRILATERAL_LABEL(QUADRILATERALS), a label for
%    each quadrilateral.
%
%    Output, integer TETRAHEDRON_VERTEX(4,TETRAHEDRONS), the vertices that
%    form each tetrahedron.
%
%    Output, integer TETRAHEDRON_LABEL(TETRAHEDRONS), a label for
%    each tetrahedron.
%
%    Output, integer HEXAHEDRON_VERTEX(8,HEXAHEDRONS), the vertices that form
%    each hexahedron.
%
%    Output, integer HEXAHEDRON_LABEL(HEXAHEDRONS), a label for each hexahedron.
%

%
%  Initialize everything to nothing.
%
  vertex_coordinate = [];
  vertex_label = [];
  edge_vertex = [];
  edge_label = [];
  triangle_vertex = [];
  triangle_label = [];
  quadrilateral_vertex = [];
  quadrilateral_label = [];
  tetrahedron_vertex = [];
  tetrahedron_label = [];
  hexahedron_vertex = [];
  hexahedron_label = [];
%
%  Open the file.
%
  unit = fopen ( filename, 'rt' );

  if ( unit < 0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'MESH_DATA_READ - Error!\n' );
    fprintf ( 1, '  Could not open the input file "%s".\n', filename );
    error ( 'MESH_DATA_READ - Error!' );
    return
  end
%
%  Read lines til you get alphanumerics and determine a "mode"
%
  line_num = 0;
  keyword = 'NONE';

  while ( 1 )

    text = fgetl ( unit );

    if ( text == -1 )
      break
    end

    line_num = line_num + 1;

    if ( s_len_trim ( text ) == 0 )
      keyword = 'NONE';
      continue
    end

    if ( text(1) == '#' )
      continue
    end
%
%  Remove initial blanks.
%

%
%  Expecting a keyword.
%
        if ( s_eqi ( text, 'CORNERS' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'DIMENSION' ) )

      keyword = 'DIMENSION';

    elseif ( s_eqi ( text, 'EDGES' ) )

      keyword = 'EDGES';

    elseif ( s_eqi ( text, 'END' ) )

      fprintf ( 1, '\n' );
      fprintf ( 1, '  END statement encountered.\n' );
      break

    elseif ( s_eqi ( text, 'HEXAHEDRA' ) || s_eqi ( text, 'HEXAHEDRONS' ) )

      keyword = 'HEXAHEDRONS';

    elseif ( s_begin ( text, 'MESHVERSIONFORMATTED' ) )

    elseif ( s_eqi ( text, 'NORMALATQUADRILATERALVERTICES' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'NORMALATTRIANGLEVERTICES' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'NORMALATVERTICES' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'NORMALS' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'QUADRILATERALS' ) )

      keyword = 'QUADRILATERALS';

    elseif ( s_eqi ( text, 'REQUIREDEDGES' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'REQUIREDVERTICES' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'RIDGES' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'TANGENTATEDGES' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'TANGENTS' ) )

      keyword = 'SKIP';

    elseif ( s_eqi ( text, 'TETRAHEDRA' ) || s_eqi ( text, 'TETRAHEDRONS' ) )

      keyword = 'TETRAHEDRONS';

    elseif ( s_eqi ( text, 'TRIANGLES' ) )

      keyword = 'TRIANGLES';

    elseif ( s_eqi ( text, 'VERTICES' ) )

      keyword = 'VERTICES';
%
%  Presumably, numeric data to be processed by keyword.
%
    elseif ( s_eqi ( keyword, 'DIMENSION' ) )

      [ value, count ] = sscanf ( text, '%d' );

      if ( count == 1 )
%       dim = value;
      end

      keyword = 'NONE';

    elseif ( s_eqi ( keyword, 'EDGES' ) )

      [ value, count ] = sscanf ( text, '%d' );

      if ( count == 1 )
%       edges = value;
      end

      keyword = 'EDGE_VERTEX';
      edge = 0;
      edge_vertex = zeros ( 2, edges );
      edge_label = zeros ( 1, edges );

    elseif ( s_eqi ( keyword, 'EDGE_VERTEX' ) )

      [ value, count ] = sscanf ( text, '%d  %d  %d' );

      edge = edge + 1;
      edge_vertex(1:2,edge) = value(1:2);
      edge_label(edge) = value(3);

    elseif ( s_eqi ( keyword, 'HEXAHEDRONS' ) )

      [ value, count ] = sscanf ( text, '%d' );

      if ( count == 1 )
%       hexahedrons = value;
      end

      keyword = 'HEXAHEDRON_VERTEX';
      hexahedron = 0;
      hexahedron_vertex = zeros ( 8, hexahedrons );
      hexahedron_label = zeros ( 1, hexahedrons );

    elseif ( s_eqi ( keyword, 'HEXAHEDRON_VERTEX' ) )

      [ value, count ] = sscanf ( text, '%d  %d  %d  %d  %d  %d  %d  %d  %d' );

      hexahedron = hexahedron + 1;
      hexahedron_vertex(1:8,hexahedron) = value(1:8);
      hexahedron_label(hexahedron) = value(9);

    elseif ( s_eqi ( keyword, 'QUADRILATERALS' ) )

      [ value, count ] = sscanf ( text, '%d' );

      if ( count == 1 )
%       quadrilaterals = value;
      end

      keyword = 'QUADRILATERAL_VERTEX';
      quadrilateral = 0;
      quadrilateral_vertex = zeros ( 4, quadrilateral );
      quadrilateral_label = zeros ( 1, quadrilateral );

    elseif ( s_eqi ( keyword, 'QUADRILATERAL_VERTEX' ) )

      [ value, count ] = sscanf ( text, '%d  %d  %d  %d  %d' );

      quadrilateral = quadrilateral + 1;
      quadrilateral_vertex(1:4,quadrilateral) = value(1:4);
      quadrilateral_label(quadrilateral) = value(5);

    elseif ( s_eqi ( keyword, 'TETRAHEDRONS' ) )

      [ value, count ] = sscanf ( text, '%d' );

      if ( count == 1 )
%       tetrahedrons = value;
      end

      keyword = 'TETRAHEDRON_VERTEX';
      tetrahedron = 0;
      tetrahedron_vertex = zeros ( 4, tetrahedron );
      tetrahedron_label = zeros ( 1, tetrahedron );

    elseif ( s_eqi ( keyword, 'TETRAHEDRON_VERTEX' ) )

      [ value, count ] = sscanf ( text, '%d  %d  %d  %d  %d' );

      tetrahedron = tetrahedron + 1;
      tetrahedron_vertex(1:4,tetrahedron) = value(1:4);
      tetrahedron_label(tetrahedron) = value(5);

    elseif ( s_eqi ( keyword, 'TRIANGLES' ) )

      [ value, count ] = sscanf ( text, '%d' );

      if ( count == 1 )
%       triangles = value;
      end

      keyword = 'TRIANGLE_VERTEX';
      triangle = 0;
      triangle_vertex = zeros ( 3, triangle );
      triangle_label = zeros ( 1, triangle );

    elseif ( s_eqi ( keyword, 'TRIANGLE_VERTEX' ) )

      [ value, count ] = sscanf ( text, '%d  %d  %d  %d' );

      triangle = triangle + 1;
      triangle_vertex(1:3,triangle) = value(1:3);
      triangle_label(triangle) = value(4);

    elseif ( s_eqi ( keyword, 'VERTICES' ) )

      [ value, count ] = sscanf ( text, '%d' );

      if ( count == 1 )
%       vertices = value;
      end

      keyword = 'VERTEX_COORDINATE';
      vertex = 0;
      vertex_coordinate = zeros ( dim, vertices );
      vertex_label = zeros ( 1, vertices );

    elseif ( s_eqi ( keyword, 'VERTEX_COORDINATE' ) )

      if ( dim == 2 )
        [ value, count ] = sscanf ( text, '%f  %f  %d' );
      elseif ( dim == 3 )
        [ value, count ] = sscanf ( text, '%f  %f  %f  %d' );
      end

      vertex = vertex + 1;
      vertex_coordinate(1:dim,vertex) = value(1:dim);
      vertex_label(vertex) = value(dim+1);

    elseif ( s_eqi ( keyword, 'SKIP' ) )

    else

      fprintf ( 1, '\n' );
      fprintf ( 1, 'MESH_DATA_READ - Fatal error!\n' );
      fprintf ( 1, '  Could not find keyword while reading line %d:\n', line_num );
      fprintf ( 1, '"%s"\n', text );
      error ( 'MESH_DATA_READ - Fatal error!\n' );

    end
  end
%
%  Close the file.
%
  fclose ( unit );

  fprintf ( 1, '\n' );
  fprintf ( 1, '  Read %d lines from "%s".\n', line_num, filename );

  return
end


function [StopFlag, Status] = StopCritDuplo(nfxk,Niter, Nmap, T, MaxNumIter, MaxNumMapEval, TimeLimit, epsilon, Stopping_Crit)
% Function checking that one of the stopping criteria
% holds to terminate LLM and GLM. It perepares the status determining why
% the algorithm is stopped.
%
% USAGE:
%
%    [StopFlag, Status] = StopCritDuplo(nfxk,Niter, Nmap, T, MaxNumIter, MaxNumMapEval, TimeLimit, epsilon, Stopping_Crit)
%
% INPUTS:
%    nhxk:             the norm 2 of `h(xk)`
%    Niter:            the number of iterations
%    Nmap:             the number of mapping calls
%    T:                the running time
%    MaxNumIter:       maximum number of iterations
%    MaxNumMapEval:    maximum number of function evaluations
%    TimeLimit:        maximum running time
%    epsilon:          accuracy parameter
%    Stopping_Crit:    stopping criterion:
%
%                        1. stop if :math:`||nfxk|| \leq \epsilon`
%                        2. stop if `MaxNumIter` is reached
%                        3. stop if `MaxNumMapEval` is reached
%                        4. stop if `TimeLimit` is reached
%                        5. stop if (default) :math:`||hxk|| \leq \epsilon` or `MaxNumIter` is reached
% OUTPUTS:
%    StopFlag:         1: if one of the stopping criteria holds, 0: if none of the stopping criteria holds
%    Status:           the reason of the scheme termination

switch Stopping_Crit

  case 1
    if nhxk <= epsilon
      StopFlag = 1;
      Status   = 'A solution of nonlinear system is found.';
    else
      StopFlag = 0;
      Status   = [];
    end

  case 2
    if Niter >= MaxNumIter
      StopFlag = 1;
      Status   = 'Maximum number of iterations is reached.';
    else
      StopFlag = 0;
      Status   = [];
    end

  case 3
    if Nmap >= MaxNumMapEval
      StopFlag = 1;
      Status   = 'Maximum number of mapping evaluations is reached.';
    else
      StopFlag = 0;
      Status   = [];
    end

  case 4
    if T >= TimeLimit
      StopFlag = 1;
      Status   = 'Time limit is reached.';
    else
      StopFlag = 0;
      Status   = [];
    end

  case 5
    if (nfxk <= epsilon || Niter >= MaxNumIter)
      StopFlag = 1;
      if Niter < MaxNumIter
          Status = 'a solution is found.';
      else
          Status = 'Maximum number of iterations is reached.';
      end
    else
      StopFlag = 0;
      Status   = [];
    end

end

end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%% End of StopCritDuplo.m %%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


function  [rankFR, rankFRV, rankFRvanilla, rankFRVvanilla, model] = checkRankFR(model, printLevel)
% Calculates the rank of `[F R]` and `[F; R]`, when restricted to certain
% rows and columns
%
% USAGE:
%
%    [rankFR, rankFRV, rankFRvanilla, rankFRVvanilla, model] = checkRankFR(model, printLevel)
%
% INPUTS:
%    model:             model structure
%    printLevel:        verbose level
%
% OUTPUTS:
%    rankFR:            rank of [`F R`], when using only `FRrows`
%    rankFRV:           rank of [`F; R`], when using only `FRVcols`
%    rankFRvanilla:     rank of [`F R`], when using all rows
%    rankFRVvanilla:    rank of [`F; R`], when using all cols
%    model:             structure with fields:
%
%                         * .FRrows - `m x 1` boolean of rows of [`F R`] that are nonzero,
%                           unique upto positive scaling and part of the
%                           maximal conservation vector
%                         * .FRVcols - `n x 1` boolean of cols of [`F; R`] that are nonzero,
%                           unique upto positive scaling and part of the
%                           maximal conservation vector
%                         * .FRirows - `m x 1` boolean of rows of [`F R`] that are independent
%                         * .FRdrows - `m x 1` boolean of rows of [`F R`] that are dependent
%                         * .FRwrows - `m x 1` boolean of independent rows of [`F R`] that
%                           have dependent rows amongst `model.FRdrows`
%                         * .FRVdcols - `n x 1` boolean of cols of [`F; R`] that are dependent
%                         * model.SConsistentMetBool - `m x 1` boolean vector indicating metabolites involved
%                           in the maximal consistent vector
%                         * .SConsistentRxnBool - `n x 1` boolean vector indicating metabolites involved
%                           in the maximal consistent vector
%                         * .FRnonZeroBool - `m x 1` boolean vector indicating metabolites involved
%                           in at least one internal reaction
%                         * .FRuniqueBool - `m x 1` boolean vector indicating metabolites with
%                           reaction stoichiometry unique upto scalar multiplication
%                         * .SIntRxnBool - `n x 1` boolean vector indicating the non exchange reactions
%                         * .FRVnonZeroBool - `n x 1` boolean vector indicating non exchange reactions
%                           with at least one metabolite involved
%                         * .FRVuniqueBool - `n x 1` boolean vector indicating non exchange reactions
%                           with stoichiometry unique upto scalar multiplication
%                         * .connectedRowsFRBool - `m x 1` boolean vector indicating metabolites in connected rows of [`F R`]
%                         * .connectedRowsFRVBool - `n x 1` boolean vector indicating complexes in connected columns of [`F; R`]
%                         * .V - :math:`S V = 0`; :math:`1^T |V| > 1` for all flux consistent reactions

if ~exist('printLevel','var')
    printLevel=1;
end

%scaling of stoichiometric matrix may be necessary when badly scaled, off
%by default
scaling='none';
%scaling='gmscal';
%scaling='minimumCoefficient';
switch scaling
    case 'minimumCoefficient'
        %rescale stoichiometric coefficients
        A=abs(model.S);
        minCoefficient=min(min(A(model.S~=0)));
        model.S=model.S*(1000/minCoefficient);
        model.lb=model.lb*(1000/minCoefficient);
        model.ub=model.ub*(1000/minCoefficient);
    case 'gmscal'
        %geometric mean scaling
        [cscale,rscale] = gmscal(model.S,0,0.9);

        % model.S =diag(rscale)*model.S*diag(cscale);
        % model.b =diag(rscale)*model.b;
        % model.lb=diag(cscale)*model.lb;
        % model.ub=diag(cscale)*model.ub;
        % model.c =diag(cscale)*model.c;

        % only scale columns
        model.S =model.S*diag(cscale);
        model.b =model.b;
        model.lb=diag(cscale)*model.lb;
        model.ub=diag(cscale)*model.ub;
        model.c =diag(cscale)*model.c;
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%vanilla forward and reverse half stoichiometric matrices
F       = -model.S;
F(F<0)  =    0;
R       =  model.S;
R(R<0)  =    0;

%vanilla ranks
[rankFRvanilla,~,~]      = getRankLUSOL([F R]);
[rankFRVvanilla,~,~]     = getRankLUSOL([F;R]);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[nMet,nRxn]=size(model.S);

if ~isfield(model,'SIntRxnBool')  || ~isfield(model,'SIntMetBool')
    %finds the reactions in the model which export/import from the model
    %boundary i.e. mass unbalanced reactions
    %e.g. Exchange reactions
    %     Demand reactions
    %     Sink reactions
    model = findSExRxnInd(model);
end
model.SIntRxnBool_findSExRxnInd=model.SIntRxnBool;

%mass and charge balance
if isfield(model,'metFormulas')
    [massImbalance,imBalancedMass,imBalancedCharge,imBalancedRxnBool,Elements,missingFormulaeBool,balancedMetBool]...
        = checkMassChargeBalance(model,printLevel);
    model.balancedRxnBool=~imBalancedRxnBool;
    model.balancedMetBool=balancedMetBool;
    model.Elements=Elements;
    model.missingFormulaeBool=missingFormulaeBool;

    if nnz(model.missingFormulaeBool)<size(model.S,1)
        %assumes that all mass imbalanced reations are exchange reactions
        model.SIntRxnBool = model.SIntRxnBool & model.balancedRxnBool;
        model.SIntMetBool = model.SIntMetBool & model.balancedMetBool;
    end
end

%stoichiometric consistency
if ~isfield(model,'SConsistentMetBool') || ~isfield(model,'SConsistentRxnBool')
    %finds the metabolites in the model that are stoichiometrically
    %consistent

    % method.interface='solveCobraLP';
    % method.solver='mosek';
    % method.param.MSK_IPAR_OPTIMIZER='MSK_OPTIMIZER_DUAL_SIMPLEX';
    method.interface='solveCobraLP';
    method.solver='gurobi';
    method.param.Method=1;

    [inform,m,model]=checkStoichiometricConsistency(model,printLevel-1,method);

    %assumes that all stoichiometrically inconsistent reactions are exchange reactions
    model.SIntRxnBool = model.SIntRxnBool & model.SConsistentRxnBool;
    model.SIntMetBool = model.SIntMetBool & model.SConsistentMetBool;
end

if printLevel>0
    fprintf('%u%s\n',nnz(~model.SIntRxnBool),' exchange reactions (imbalanced elementally, or involves a stoichiometrically inconsistent molecular species)')
    for i=1:nRxn
        if ~model.SIntRxnBool(i) && model.SIntRxnBool_findSExRxnInd(i)
            fprintf('%s%s\n',model.rxns{i},': deemed an exchange reaction')
        end
    end
    fprintf('---------\n')
end

if any(~model.SIntMetBool) & printLevel>0
    fprintf('%u%s\n',nnz(~model.SIntMetBool),' rows of S only involved in exchange reactions')
    for i=1:nMet
        if ~model.SIntMetBool(i)
            fprintf('%s%s\n',model.mets{i}',': deemed a molecular species only involved in exchange reactions')
        end
    end
    fprintf('---------\n')
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%metBool1=(model.SConsistentMetBool | ~model.SIntMetBool); %incorrect to
%include mets that are only exchanged
metBool1=model.SConsistentMetBool;
rxnBool1=(model.SConsistentRxnBool | ~model.SIntRxnBool);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%find rows that are not all zero when a subset of reactions omitted
A1=[F(:,rxnBool1) R(:,rxnBool1)];
model.FRnonZeroRowBool1 = any(A1,2);

%find cols that are not all zero when a subset of metabolites omitted
A1=[F(metBool1,:); R(metBool1,:)];
model.FRnonZeroColBool1 = any(A1,1)';

%only report for the consistent rows
if any(~model.FRnonZeroRowBool1 & metBool1) & printLevel>0
    fprintf('%u%s\n',nnz(~model.FRnonZeroRowBool1 & metBool1),' zero rows of [F,R]')
    for i=1:nMet
        if ~model.FRnonZeroRowBool1(i) && metBool1(i)
            fprintf('%s%s\n',model.mets{i},': is a zero row of [F,R]')
        end
    end
    fprintf('\n')
end

%only report for the consistent cols
if any(~model.FRnonZeroColBool1 & rxnBool1) && 0
    fprintf('%u%s\n',nnz(~model.FRnonZeroColBool1 & rxnBool1),' zero cols of consistent [F;R]')
    for i=1:nRxn
        if ~model.FRnonZeroColBool1(i) && rxnBool1(i)
            fprintf('%s%s\n',model.rxns{i},': is a zero col of consistent [F;R]')
        end
    end
    fprintf('\n')
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
A=[F,R];%depends on the direction of reaction

%detect the rows of A that are identical upto scalar multiplication
%divide each row by the sum of each row.
sumA2            = sum(A,2);
sumA2(sumA2==0) = 1;
normalA2          = diag(1./sumA2)*A;

%get unique rows, but do not change the order
% [C,IA,IC] = unique(A,'rows') also returns index vectors IA and IC such
% that C = A(IA,:) and A = C(IC,:).
[uniqueRowsA,IA,IC] = unique(normalA2,'rows','stable');
model.FRuniqueRowBool=zeros(nMet,1);
model.FRuniqueRowBool(IA)=1;

if any(~model.FRuniqueRowBool & metBool1) & printLevel>0
    fprintf('%u%s\n',nnz(~model.FRuniqueRowBool & metBool1),' non-unique rows of stoich consistent [F,R]')
    for i=1:nMet
        if ~model.FRuniqueRowBool(i) && metBool1(i)
            fprintf('%s%s\n',model.mets{i},': not unique row of stoich consistent [F,R]')
        end
    end
    fprintf('\n')
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
A=F+R;%invariant to direction of reaction

%detect the cols of A that are identical upto scalar multiplication
%divide each col by the sum of each row.
sumA1          = sum(A,1);
sumA1(sumA1==0)  = 1;
normalA1          = A*diag(1./sumA1);

%get unique cols, but do not change the order
% [C,IA,IC] = unique(A,'rows') also returns index vectors IA and IC such
% that C = A(IA,:) and A = C(IC,:).
[uniqueColsA,IA,IC] = unique(normalA1','rows','stable');
model.FRuniqueColBool=zeros(nRxn,1);
model.FRuniqueColBool(IA)=1;

if any(~model.FRuniqueColBool & rxnBool1) & printLevel>0
    fprintf('%u%s\n',nnz(~model.FRuniqueColBool & rxnBool1),' non-unique cols of stoich consistent [F;R]')
    for i=1:nRxn
        if ~model.FRuniqueColBool(i) && rxnBool1(i)
            fprintf('%s%s\n',model.rxns{i},': not unique col of stoich consistent [F;R]')
        end
    end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if printLevel>0
    fprintf('\n%s\n','Diagnostics on size of boolean vectors for rows:')
    fprintf('%s%u\n','SIntMetBool:                 ',nnz(model.SIntMetBool))
    fprintf('%s%u\n','SConsistentMetBool:          ',nnz(model.SConsistentMetBool))
    fprintf('%s%u\n','FRnonZeroRowBool1:               ',nnz(model.FRnonZeroRowBool1))
    fprintf('%s%u\n','FRuniqueRowBool:               ',nnz(model.FRuniqueRowBool))

    fprintf('\n%s\n','Diagnostics on size of boolean vectors for cols:')
    fprintf('%s%u\n','SIntRxnBool:                 ',nnz(model.SIntRxnBool))
    fprintf('%s%u\n','SConsistentRxnBool:          ',nnz(model.SConsistentRxnBool))
    fprintf('%s%u\n','FRnonzeroColBool:               ',nnz(model.FRnonZeroColBool1))
    fprintf('%s%u\n','FRuniqueColBool:               ',nnz(model.FRuniqueColBool))
    fprintf('\n')
end

if ~isempty(find(strcmp(model.mets(~model.SConsistentMetBool),'h[c]')))
    warning('h[c] is one of the inconsistent molecules')
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
metBool2=model.SConsistentMetBool & model.FRuniqueRowBool & model.FRnonZeroRowBool1;

% rxnBool2=(model.SConsistentRxnBool | ~model.SIntRxnBool);
rxnBool2=(model.SConsistentRxnBool | ~model.SIntRxnBool) & model.FRuniqueColBool;

%the uniqueness check has to be done before flux consistency since some
%models have a forward and backward reaction in separately, but these are
%the same reaction when both considered reversible.

%from the unique exchange reactions, and the subset of the unique non-exchange
%reactions that form a matrix with stoichiometrically consistent rows,
%check for reactions that are also flux consistent, with all reactions
%reversible
modelRev.S = model.S(metBool2,rxnBool2);

%infinite bounds may be necessary when stoichiometric matrix is badly
%scaled, in that case the minimum flux required (epsilon) is set to 1, the
%default lower and upper bounds are {-1000, 1000}, with epsilon getCobraSolverParams('LP', 'feasTol')*100,
%as that is two orders of magnitude higher than the typical feasibility
%tolerance for volation of mass balance constraints.
fluxConsistencyBounds='aThousand';
switch fluxConsistencyBounds
    case 'aThousand'
        %use all reactions reversible
        modelRev.lb=-1000*ones(size(modelRev.S,2),1);
        modelRev.ub= 1000*ones(size(modelRev.S,2),1);
        epsilon = getCobraSolverParams('LP', 'feasTol')*100;
    case 'infinity'
        %use all reactions reversible
        modelRev.lb=-inf*ones(size(modelRev.S,2),1);
        modelRev.ub= inf*ones(size(modelRev.S,2),1);
        epsilon = 1;
    case 'reconstruction'
        %use reconstruction reactions
        modelRev.lb=model.lb(rxnBool2);
        modelRev.ub=model.ub(rxnBool2);
        epsilon = getCobraSolverParams('LP', 'feasTol')*100;
end
modelRev.mets=model.mets(metBool2);
modelRev.rxns=model.rxns(rxnBool2);

%fast consistency check code from Nikos Vlassis et al
modeFlag=1;
[indFluxConsist,~,V0]=fastcc(modelRev,epsilon,printLevel-1,modeFlag);
modelRev.fluxConsistentRxnBool=false(size(modelRev.S,2),1);
modelRev.fluxConsistentRxnBool(indFluxConsist)=1;

%build the vector the same size as the original S
model.fluxConsistentRxnBool=false(nRxn,1);
%assign the boolean state to the original size model
model.fluxConsistentRxnBool(rxnBool2)=modelRev.fluxConsistentRxnBool;

%pad out V0 to correspond to the original size of S
model.V=sparse(nRxn,size(V0,2));
model.V(rxnBool2,:)=V0;

%check to make sure booleans are correct
if nnz(model.SConsistentRxnBool & ~model.SIntRxnBool)~=0
    error('No exchange reaction should be stoichiometrically consistent')
end

%check to make sure that V in nullspace of S.
tmp=norm(full(model.S(metBool2,model.SConsistentRxnBool & model.SIntRxnBool)*model.V(model.SConsistentRxnBool & model.SIntRxnBool,:)...
       + model.S(metBool2,~model.SIntRxnBool)*model.V(~model.SIntRxnBool,:)));
%two extra digits spare
if tmp>100*epsilon
    disp(tmp)
    error('Not flux consistent')
end
%create a stoichiometric matrix of perpetireactions only for
%stoichiometrically and flux consistent part
model.P=sparse(nMet,size(V0,2));
model.P(metBool2,:)=model.S(metBool2,~model.SIntRxnBool)*model.V(~model.SIntRxnBool,:);

% model.fluxConsistentRxnBool=true(nRxn,1);

%rows corresponding to flux consistent reactions
model.fluxConsistentMetBool = sum(model.S(:,model.fluxConsistentRxnBool)~=0,2)~=0;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%eliminate the metabolites and reactions that are stoichiometrically
%inconsistent or flux inconsistent from further consideration, but keep the
%flux consistent exchange reactions, also eliminate scalar multiples
metBool3=model.SConsistentMetBool & model.fluxConsistentMetBool & model.FRuniqueRowBool & model.FRnonZeroRowBool1;
rxnBool3=(model.SConsistentRxnBool | ~model.SIntRxnBool)  & model.FRuniqueColBool & model.fluxConsistentRxnBool;

%find rows that are not all zero when a subset of reactions omitted
A3=[F(:,rxnBool3) R(:,rxnBool3)];
model.FRnonZeroRowBool = any(A3,2);

%find cols that are not all zero when a subset of metabolites omitted
A3=[F(metBool3,:); R(metBool3,:)];
model.FRnonZeroColBool = any(A3,1)';

%only report for the latest subset of rows
if any(~model.FRnonZeroRowBool & metBool3) & printLevel>0
    fprintf('%u%s\n',nnz(~model.FRnonZeroRowBool & metBool3),' zero rows of [F,R]')
    for i=1:nMet
        if ~model.FRnonZeroRowBool(i) && metBool3(i)
            fprintf('%s%s\n',model.mets{i},': is a zero row of [F,R]')
        end
    end
    fprintf('\n')
end

%only report for the latest subset of cols
if any(~model.FRnonZeroColBool & rxnBool3) && 0
    fprintf('%u%s\n',nnz(~model.FRnonZeroColBool & rxnBool3),' zero cols of consistent [F;R]')
    for i=1:nRxn
        if ~model.FRnonZeroColBool(i) && rxnBool3(i)
            fprintf('%s%s\n',model.rxns{i},': is a zero col of consistent [F;R]')
        end
    end
    fprintf('\n')
end

%pause(eps)

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% A4=F(:,rxnBool3)+R(:,rxnBool3);%invariant to direction of reaction
%
% %detect the cols of A that are identical upto scalar multiplication
% %divide each col by the sum of each row.
% sumA4             = sum(A4,1);
% sumA4(sumA4==0)   = 1;
% normalA4          = A4*diag(1./sumA4);
%
% %get unique cols, but do not change the order
% % [C,IA,IC] = unique(A,'rows') also returns index vectors IA and IC such
% % that C = A(IA,:) and A = C(IC,:).
% [uniqueColsA,IA,IC] = unique(normalA4','rows','stable');
% model.FRuniqueColBool2=zeros(nRxn,1);
% model.FRuniqueColBool2(IA)=1;
%
% if any(~model.FRuniqueColBool2 & rxnBool3)
%     fprintf('%u%s\n',nnz(~model.FRuniqueColBool2 & rxnBool3),' non-unique cols of consistent [F;R]')
%     for i=1:nRxn
%         if ~model.FRuniqueColBool(i) && rxnBool1(i)
%             fprintf('%s%s\n',model.rxns{i},': not unique col of consistent [F;R]')
%         end
%     end
% end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% The largest connected component - not necessary, flux consistency takes
% care of this connected rows of [F,R] and columns of [F;R]
% [model.connectedRowsFRBool,model.connectedColsFRVBool]=connectedFR(F,R);
% [~,maxInd]=max(sum(model.connectedRowsFRBool,1));
% model.largestConnectedRowsFRBool=model.connectedRowsFRBool(:,maxInd);
% [~,maxInd]=max(sum(model.connectedColsFRVBool,1));
% model.largestConnectedColsFRVBool=model.connectedColsFRVBool(:,maxInd);

%bypass test for connectedness
model.largestConnectedRowsFRBool=true(nMet,1);
model.largestConnectedColsFRVBool=true(nRxn,1);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%diagnostics
if printLevel>0
    fprintf('\n%s\n','Diagnostics on size of boolean vectors for rows:')
    fprintf('%s%u\n','SConsistentMetBool:         ',nnz(model.SConsistentMetBool))
    fprintf('%s%u\n','fluxConsistentMetBool:      ',nnz(model.fluxConsistentMetBool))
    fprintf('%s%u\n','FRuniqueRowBool:               ',nnz(model.FRuniqueRowBool))
    fprintf('%s%u\n','FRnonZeroRowBool1:              ',nnz(model.FRnonZeroRowBool1))
    fprintf('%s%u\n','FRnonZeroRowBool:              ',nnz(model.FRnonZeroRowBool))
   % fprintf('%s%u\n','largestConnectedRowsFRBool: ',nnz(model.largestConnectedRowsFRBool))

    fprintf('\n%s\n','Diagnostics on size of boolean vectors for cols:')
    fprintf('%s%u\n','SIntRxnBool:                 ',nnz(model.SIntRxnBool))
    fprintf('%s%u\n','SConsistentRxnBool:          ',nnz(model.SConsistentRxnBool))
    fprintf('%s%u\n','fluxConsistentRxnBool:       ',nnz(model.fluxConsistentRxnBool))
    fprintf('%s%u\n','FRuniqueColBool:               ',nnz(model.FRuniqueColBool))
    fprintf('%s%u\n','FRnonZeroColBool1:              ',nnz(model.FRnonZeroColBool1))
    fprintf('%s%u\n','FRnonZeroColBool:              ',nnz(model.FRnonZeroColBool))
    %fprintf('%s%u\n','largestConnectedColsFRVBool: ',nnz(model.largestConnectedColsFRVBool))
    fprintf('\n')
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%only use rows that are nonzero, unique upto positive scaling, part of
%the maximal conservation vector, and part of the largest component
selection='c';
switch selection
    case 'a'
        model.FRrows = model.SConsistentMetBool...
            & model.fluxConsistentMetBool...
            & model.FRnonZeroRowBool...
            & model.FRuniqueRowBool;
        model.FRVcols = (model.SConsistentRxnBool | ~model.SIntRxnBool)...
            & model.fluxConsistentRxnBool...
            & model.FRuniqueColBool...
            & model.FRnonZeroColBool;
    case 'b'
        %exchange reactions not considered when rank([F,R]) measured
        model.FRrows = model.SConsistentMetBool...
            & model.fluxConsistentMetBool...
            & model.FRnonZeroRowBool...
            & model.FRuniqueRowBool;
        model.FRVcols = model.SConsistentRxnBool...
            & model.fluxConsistentRxnBool...
            & model.FRuniqueColBool...
            & model.FRnonZeroColBool;
    case 'c'
        model.FRrows = model.SConsistentMetBool...
            & model.fluxConsistentMetBool...
            & model.FRnonZeroRowBool1...
            & model.FRnonZeroRowBool...
            & model.FRuniqueRowBool;
        model.FRVcols = (model.SConsistentRxnBool | ~model.SIntRxnBool)...
            & model.fluxConsistentRxnBool...
            & model.FRuniqueColBool...
            & model.FRnonZeroColBool;
    case 'd'
        model.FRrows = model.SConsistentMetBool...
            & model.fluxConsistentMetBool...
            & model.FRnonZeroRowBool1...
            & model.FRnonZeroRowBool...
            & model.FRuniqueRowBool;
        model.FRVcols = (model.SConsistentRxnBool | ~model.SIntRxnBool)...
            & model.fluxConsistentRxnBool;
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%omit both the rows and the columns as specified above
Fr = F(model.FRrows,model.FRVcols);
Rr = R(model.FRrows,model.FRVcols);
Fc = F(model.FRrows,model.FRVcols);
Rc = R(model.FRrows,model.FRVcols);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% method.interface='solveCobraLP';
% method.solver='mosek';
% method.param.MSK_IPAR_OPTIMIZER='MSK_OPTIMIZER_DUAL_SIMPLEX';

method.interface='solveCobraLP';
method.solver='gurobi5';
method.param.Method=1;

modelCheck.S = Rr-Fr;
modelCheck.SIntRxnBool=model.SIntRxnBool;
modelCheck.SIntMetBool=model.SIntMetBool(model.FRrows);
[informC,mC,~]=checkStoichiometricConsistency(modelCheck,0,method);
%save check on consistency
model.mC=mC;
if informC~=1
    disp(length(mC)-nnz(mC))
    error('(R-F) not stoichiometrically consistent')
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% [A,B,C]=bilinearDecomposition(model.S(model.FRrows,model.FRVcols));
% %forward and reverse half stoichiometric matrices
% Fb        =   -B;
% Fb(Fb<0)  =    0;
% Rb        =    B;
% Rb(Rb<0)  =    0;
% Fb(Fb~=0) =    1;
% Rb(Rb~=0) =    1;
% adj = inc2adj([Fb Rb]);
%
% % Test whether a graph is bipartite, if yes, return the two vertex sets
% % Inputs: graph in the form of adjancency list (neighbor list, see adj2adjL.m)
% % Outputs: True/False (boolean), empty set (if False) or two sets of vertices
% tic
% [isit,partA,partB]=isbipartite(adj2adjL(adj));
% toc
% %pause(eps)

%check rank of bilinear version
[A,B,C]=bilinearDecomposition(Fr-Rr);
%forward and reverse half stoichiometric matrices
Frb        =   -B;
Frb(Frb<0)  =    0;
Rrb        =    B;
Rrb(Rrb<0)  =    0;

%rank of [F R]
[rankBilinearFrRr,bilinearFrRrp,bilinearFrRrq] = getRankLUSOL([Frb Rrb]);
model.Frb=Frb;
model.Rrb=Rrb;
model.rankBilinearFrRr=rankBilinearFrRr;

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

%rank of [F R]
[rankFR,FRp,FRq] = getRankLUSOL([Fr Rr]);

%indices of rows that are dependent
ind=find(model.FRrows);
iR=ind(FRp(1:rankFR));
dR=ind(FRp(rankFR+1:length(FRp)));
model.FRdrows=false(nMet,1);
model.FRdrows(dR)=1;
model.FRirows=false(nMet,1);
model.FRirows(iR)=1;
model.FRq=FRq;
model.FRp=FRp;
model.Fr=Fr;
model.Rr=Rr;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%rank of [F;R]
[rankFRV,FRVp,FRVq]  = getRankLUSOL([Fc;Rc]);

%boolean of non-exchange columns that are dependent
vertInd=find(model.FRVcols);
iC=vertInd(FRVq(1:rankFRV));
dC=vertInd(FRVq(rankFRV+1:length(FRVq)));
model.FRVdcols=false(nRxn,1);
model.FRVdcols(dC)=1;
model.FRVicols=false(nRxn,1);
model.FRVicols(iC)=1;
model.FRVq=FRVq;
model.FRVp=FRVp;
model.Fc=Fc;
model.Rc=Rc;

%sanity check
if nnz(model.FRrows) ~= nnz(model.FRrows & model.FRdrows)
    error('')
end
if nnz(model.FRVcols) ~= nnz(model.FRVcols & model.FRVdcols)
    error('')
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%examine the dependencies between rows and columns if any
model.FRrowRankDeficiency=nnz(model.FRrows)-rankFR;
if model.FRrowRankDeficiency>0
    T  = [Fr Rr];
    %code from Michael
    C1 = T(FRp(1:rankFR),:);
    C2 = T(FRp(rankFR+1:length(FRp)),:);
    W0 = C1' \ C2'; % solves LS problems min ||C1'*W_k - C2_k'||

    model.FRW=sparse(model.FRrowRankDeficiency,nMet);
    model.FRW(:,iR)=W0';%L
    model.FRW(:,dR)=-speye(model.FRrowRankDeficiency);

    %indices of independent rows that other rows are dependent on
    wR=find(sum(abs(model.FRW),1)>0)';
    model.FRwrows=false(nMet,1);
    model.FRwrows(wR)=1;

    %sanity checks
    disp(norm(C1'*W0 - C2',inf)) %should be zero
    disp(norm([W0',-speye(model.FRrowRankDeficiency)]*[C1;C2],inf)) %should be zero

    W = sparse(model.FRrowRankDeficiency,size([Fr Rr],1));
    W(:,FRp(1:rankFR))=W0';
    W(:,FRp(rankFR+1:length(FRp)))=-speye(model.FRrowRankDeficiency);
    disp(norm(W*[Fr Rr],inf)) %should be zero

    model.FRW=sparse(model.FRrowRankDeficiency,nMet);
    model.FRW(:,model.FRrows)=W;
    disp(norm(model.FRW*[F(:,model.FRVcols), R(:,model.FRVcols)],inf)) %should be zero
    %%%%% N B %%%%
    disp(norm(model.FRW*[F,R],inf)) % will not in general be zero as other columns of [F R] could contribute

    % nnz(W)
    % figure; spy(W)
    % figure; spy(abs(W) > 1e-3)  % say
end

model.FRcolRankDeficiency=nnz(model.FRVcols)-rankFRV;
if model.FRcolRankDeficiency>0
    VT  = [Fc;Rc];
    %code from Michael
    VC1  = VT(:,model.FRVq(1:rankFRV)); % independent columns
    VC2  = VT(:,model.FRVq(rankFRV+1:size(VT,2))); % dependent columns
    VW0  = VC1 \ VC2; % solves LS problems min ||VC1*WV_k - VC2_k||

    model.FRVW=sparse(nRxn,model.FRcolRankDeficiency);
    model.FRVW(iC,:)=VW0; % independent columns
    model.FRVW(dC,:)=-speye(model.FRcolRankDeficiency); % dependent columns

    %indices of independent cols that other cols are dependent on
    wC=find(sum(abs(model.FRVW),2)>0);
    model.FRVwcols=false(nRxn,1);
    model.FRVwcols(wC)=1;

    if printLevel>2
        %sanity check
        disp(norm([F(model.FRrows,:); R(model.FRrows,:)]*model.FRVW,inf))%should be zero
        %%%%% N B %%%%
        disp(norm([F;R]*model.FRVW,inf)) % will not in general be zero as other rows [F;R] could contribute
    end

    % nnz(VW0)
    % figure; spy(VW0)
    % figure; spy(abs(VW0) > 1e-3)  % say
end


function fem = update1320nbr(fem_struct,jj)
% update1320nbr takes a node with connectivity between 13 and 20
%  and adds six new nodes and uses delanunay trianglulation to
%  create the new elements. The region is then sprung. An nei is
%  not required but if one is not present, it will be generated.
%
%  Variables
%  fem_struct -- finite element structure from opnml.
%  jj -- the node number that has 12 elements connected to it.
%  fem -- the updated finite element structure.
%        Note:  Only fem.x,fem.y,fem.z,fem.e, and fem.nei
%               get updated.  fem.bnd does not need to be updated.
%
%  Usage -- fem = update1320nbr(fem_struct,jj)
%
%  FileName: update1320nbr.m
%  Written by: Ben Holladay (SEAP Student 2004)
%  Date: July 7,2004
%  Modified:  August 30, 2004 -- Chris Massey
%             June 14,2005 -- Ben Holladay
%                   Fix: Ensure that delaunay doesn't create elements
%                   outside the patch.
% Last Modifed:
%         Sept. 19, 2006 -- Chris Massey, NRL Code 7322, S.S.C. MS 39529
%                      % Add test to make sure the correct nodal neighbor
%                        connectivity existed for the requested update
%         July 14, 2008 -- Chris Massey, NRL Code 7322
%               moved test for 4 neighbors to after the test for nei.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%Checks if the fem_struct has an nei. If not one is generated.
try
    nei = fem_struct.nei;
catch
    fem_struct.nei = ele2nei(fem_struct.e,fem_struct.x,fem_struct.y);
    nei = fem_struct.nei;
end

tempnc = sum(~ismember((fem_struct.nei(jj,:)),0));
if (tempnc < 13 | tempnc > 20)
    error(['There are ',num2str(tempnc),' nodal neighbors for node number ',...
           num2str(jj),'. This routine only works for 13-20 nodal neighbors.']);
end

%Sets the intial fem_struct variables.
x = fem_struct.x;
y = fem_struct.y;
z = fem_struct.z;
enodes = fem_struct.e;

%Adds new nodes,determines connectivity, and labels the bad node,
%neighboring nodesm and neighboring elements.
badnode = jj;
[nbrelem,~] = find(enodes == badnode);
[nnodes,trnc] = size(nei);
temp1 = sum(ismember(nei(jj,:),0));
nc = trnc - temp1;
nbrnode = nei(badnode,1:nc);
newnode = nnodes + [1:6];
vod = ([1:nc,1:nc]);
inrad = max(sqrt((x(nbrnode)-x(badnode)).^2 + (y(nbrnode)-y(badnode)).^2));

%Computes the center angle of each element.
b2 = (x(nbrnode(:))-x(badnode)).^2 + (y(nbrnode(:))-y(badnode)).^2;
a2 = (x(nbrnode(:))-x(nbrnode([2:nc,1]))).^2 + (y(nbrnode(:))-y(nbrnode([2:nc,1]))).^2;
A = (180/pi)*acos((b2+b2([2:nc,1])-a2)./(2*sqrt(b2).*sqrt(b2([2:nc,1]))));
ang = A;

%Sets the patterns for how to add the nodes.
addnode(1,:) = ([2,2,2,2,2,3]);
addnode(2,:) = ([2,2,3,2,2,3]);
addnode(3,:) = ([2,3,2,3,2,3]);
addnode(4,:) = ([3,3,2,3,3,2]);
addnode(5,:) = ([2,3,3,3,3,3]);
addnode(6,:) = ([3,3,3,3,3,3]);
addnode(7,:) = ([3,3,3,3,3,4]);
addnode(8,:) = ([3,3,4,3,3,4]);

%Determines the appropriate pattern for adding nodes
addnodepat = addnode((nc - 12),:);

%Labels reference nodes to be use to determine the new nodes locations.
spb(1) = 1;
spb(2) = spb(1) + addnodepat(1);
spb(3) = spb(2) + addnodepat(2);
spb(4) = spb(3) + addnodepat(3);
spb(5) = spb(4) + addnodepat(4);
spb(6) = spb(5) + addnodepat(5);

%Determines the total angle that each node will cover.
testang(1) = sum([ang([spb(1):(spb(2)-1)])]);
testang(2) = sum([ang([spb(2):(spb(3)-1)])]);
testang(3) = sum([ang([spb(3):(spb(4)-1)])]);
testang(4) = sum([ang([spb(4):(spb(5)-1)])]);
testang(5) = sum([ang([spb(5):(spb(6)-1)])]);
testang(6) = sum([ang([spb(6):nc])]);

%Test to make sure no node is trying to cover more than half of the
%circle.
test = find(testang > 180);
while ~isempty(test)
    hightemp = find(testang > 180);
    hhtempplus = hightemp + 1;
    if hhtempplus == 7; break; end
    spb(vod(hhtempplus)) = spb(vod(hhtempplus)) - 1;
    testang(1) = sum([ang([spb(1):(spb(2)-1)])]);
    testang(2) = sum([ang([spb(2):(spb(3)-1)])]);
    testang(3) = sum([ang([spb(3):(spb(4)-1)])]);
    testang(4) = sum([ang([spb(4):(spb(5)-1)])]);
    testang(5) = sum([ang([spb(5):(spb(6)-1)])]);
    testang(6) = sum([ang([spb(6):nc])]);
    test = find(testang > 180);
end

%First guess of the new coordinates and bathymetry.
M(1,1) = sum(x([(nbrnode([spb(1):spb(2)])),badnode,badnode]));
M(1,2) = sum(y([(nbrnode([spb(1):spb(2)])),badnode,badnode]));
M(1,3) = sum(z([(nbrnode([spb(1):spb(2)])),badnode,badnode]));

M(2,1) = sum(x([(nbrnode([spb(2):spb(3)])),badnode,badnode]));
M(2,2) = sum(y([(nbrnode([spb(2):spb(3)])),badnode,badnode]));
M(2,3) = sum(z([(nbrnode([spb(2):spb(3)])),badnode,badnode]));

M(3,1) = sum(x([(nbrnode([spb(3):spb(4)])),badnode,badnode]));
M(3,2) = sum(y([(nbrnode([spb(3):spb(4)])),badnode,badnode]));
M(3,3) = sum(z([(nbrnode([spb(3):spb(4)])),badnode,badnode]));

M(4,1) = sum(x([(nbrnode([spb(4):spb(5)])),badnode,badnode]));
M(4,2) = sum(y([(nbrnode([spb(4):spb(5)])),badnode,badnode]));
M(4,3) = sum(z([(nbrnode([spb(4):spb(5)])),badnode,badnode]));

M(5,1) = sum(x([(nbrnode([spb(5):spb(6)])),badnode,badnode]));
M(5,2) = sum(y([(nbrnode([spb(5):spb(6)])),badnode,badnode]));
M(5,3) = sum(z([(nbrnode([spb(5):spb(6)])),badnode,badnode]));

M(6,1) = sum(x([(nbrnode([spb(6):nc,1])),badnode,badnode]));
M(6,2) = sum(y([(nbrnode([spb(6):nc,1])),badnode,badnode]));
M(6,3) = sum(z([(nbrnode([spb(6):nc,1])),badnode,badnode]));

M(7,1) = x(badnode);
M(7,2) = y(badnode);
M(7,3) = z(badnode);

M(1,:) = M(1,:) ./ (addnodepat(1) + 3);
M(2,:) = M(2,:) ./ (addnodepat(2) + 3);
M(3,:) = M(3,:) ./ (addnodepat(3) + 3);
M(4,:) = M(4,:) ./ (addnodepat(4) + 3);
M(5,:) = M(5,:) ./ (addnodepat(5) + 3);
M(6,:) = M(6,:) ./ (addnodepat(6) + 3);

x([([newnode(1:6)]),badnode]) = ([M(:,1)]);
y([([newnode(1:6)]),badnode]) = ([M(:,2)]);
z([([newnode(1:6)]),badnode]) = ([M(:,3)]);

%Uses delaunay trianglulation to created a new and updated element list.
tempx = x([badnode,newnode(:)',nbrnode(:)']);
tempy = y([badnode,newnode(:)',nbrnode(:)']);
tri = delaunay(tempx,tempy);
tri2 = tri;

%Convertst the local element list from tri back to its global numberiing.
for i = 1:nc+7
    temp = find(i == tri);
    tmp = ([badnode,newnode(:)',nbrnode(:)']);
    tri2(temp) = tmp(i);
end

%Checks if the elements are in CCW order.
xnodes = x(tri2);
ynodes = y(tri2);
temparea = 0.5*(xnodes(:,1).*(ynodes(:,2)-ynodes(:,3))+xnodes(:,2).*...
    (ynodes(:,3)-ynodes(:,1))+xnodes(:,3).*(ynodes(:,1)-ynodes(:,2)));
Itri = find(temparea < 0);
tri3 = tri2;
tri3(Itri,2) = tri2(Itri,3);
tri3(Itri,3) = tri2(Itri,2);

%Removes elements that would be created outside the patch.
%!!MODIFICATION!!
oldbnd = detbndy(enodes(nbrelem,:));
newbnd = detbndy(tri3);
oldbnd = sort(oldbnd,2);
newbnd = sort(newbnd,2);
temp11 = find(ismember(newbnd,oldbnd,'rows') ~= 1);
while isempty(temp11) == 0
    newbnd = detbndy(tri3);
    newbnd = sort(newbnd,2);
    temp11 = find(ismember(newbnd,oldbnd,'rows') ~= 1);
    temp12 = newbnd(temp11,:);
    for it = 1:size(temp12,1)
        [tr,tc] = find(tri3 == temp12(it,1) | tri3 == temp12(it,2));
        [ta,tb,tc] = unique(tr);
        temp13 = setdiff(1:length(tr),tb);
        temp14 = tr(temp13);
        tri3(temp14,:) = [];
    end
end

%Finds and throws out bad elements and adds new elements to the global list.
tempa = setdiff(1:1:length(enodes),nbrelem);
enodes = enodes(tempa,1:3);
nelems = size(enodes,1);
tri3r = size(tri3,1);
enodes([(nelems + 1):(nelems + tri3r)],:) = tri3;

%Springs the region.
stoptol = 10e-8 * inrad;
tol = stoptol + 10;
i = 1;
imax = 20;
while i <= imax && tol > stoptol
    M2(7,1) = sum([M(2,1),M(1,1),M(3,1),M(5,1),M(4,1),M(6,1)]);
    M2(7,2) = sum([M(2,2),M(1,2),M(3,2),M(5,2),M(4,2),M(6,2)]);
    M2(7,3) = sum([M(2,3),M(1,3),M(3,3),M(5,3),M(4,3),M(6,3)]);

    [r1,c1] = find(enodes == newnode(1));
    nbr1 = unique(enodes(r1,:));
    temp1 = find(nbr1 == newnode(1));
    nbr1(temp1) = [];
	M2(1,1) = sum(x([nbr1]));
    M2(1,2) = sum(y([nbr1]));
    M2(1,3) = sum(z([nbr1]));

    [r2,c] = find(enodes == newnode(2));
    nbr2 = unique(enodes(r2,:));
    temp2 = find(nbr2 == newnode(2));
    nbr2(temp2) = [];
	M2(2,1) = sum(x([nbr2]));
    M2(2,2) = sum(y([nbr2]));
    M2(2,3) = sum(z([nbr2]));

    [r3,c3] = find(enodes == newnode(3));
    nbr3 = unique(enodes(r3,:));
    temp3 = find(nbr3 == newnode(3));
    nbr3(temp3) = [];
	M2(3,1) = sum(x([nbr3]));
    M2(3,2) = sum(y([nbr3]));
    M2(3,3) = sum(z([nbr3]));

    [r4,c4] = find(enodes == newnode(4));
    nbr4 = unique(enodes(r4,:));
    temp4 = find(nbr4 == newnode(4));
    nbr4(temp4) = [];
	M2(4,1) = sum(x([nbr4]));
    M2(4,2) = sum(y([nbr4]));
    M2(4,3) = sum(z([nbr4]));

    [r5,c5] = find(enodes == newnode(5));
    nbr5 = unique(enodes(r5,:));
    temp5 = find(nbr5 == newnode(5));
    nbr5(temp5) = [];
	M2(5,1) = sum(x([nbr5]));
    M2(5,2) = sum(y([nbr5]));
    M2(5,3) = sum(z([nbr5]));

	[r6,c6] = find(enodes == newnode(6));
    nbr6 = unique(enodes(r6,:));
    temp6 = find(nbr6 == newnode(6));
    nbr6(temp6) = [];
	M2(6,1) = sum(x([nbr6]));
    M2(6,2) = sum(y([nbr6]));
    M2(6,3) = sum(z([nbr6]));

    M2(1,:) = M2(1,:) ./ (size((nbr1),1));
    M2(2,:) = M2(2,:) ./ (size((nbr2),1));
    M2(3,:) = M2(3,:) ./ (size((nbr3),1));
    M2(4,:) = M2(4,:) ./ (size((nbr4),1));
    M2(5,:) = M2(5,:) ./ (size((nbr5),1));
    M2(6,:) = M2(6,:) ./ (size((nbr6),1));
    M2(7,:) = M2(7,:) ./ 6;

	tol = max(sqrt((M2(:,1)-M(:,1)).^2 + (M2(:,2)-M(:,2)).^2));

    M = M2;
    x([([newnode(1:6)]),badnode]) = ([M(:,1)]);
    y([([newnode(1:6)]),badnode]) = ([M(:,2)]);
    z([([newnode(1:6)]),badnode]) = ([M(:,3)]);
    i = i + 1;
end

%Updates the nei for the region.
tempnode = ([newnode,badnode,nbrnode]);
tempcount = size(tempnode,2);
for i = 1:tempcount
    [tr,tc] = find(tempnode(i) == enodes);
    tempnbr = unique(enodes(tr,:));
    tmp1 = find(tempnbr == tempnode(i));
    tempnbr(tmp1) = [];
    tempnbrx = x(tempnbr) - x(tempnode(i));
    tempnbry = y(tempnbr) - y(tempnode(i));
    angle = (atan2(tempnbry,tempnbrx))*(180/pi);
    tempang = find(angle < 0);
    angle(tempang) = angle(tempang) + 360;
    [tmp2,tempi] = sort(angle);
    tempnbr1 = tempnbr(tempi);
    tmpr = size(tempnbr,1);
    nei(tempnode(i),:) = ([tempnbr1(1:tmpr)',zeros(1,trnc-tmpr)]);
end

%Createst the output structure.
fem = fem_struct;
fem.x = x;
fem.y = y;
fem.z = z;
fem.e = enodes;
fem.nei = nei;
return


function [fx,tt]=fxrapt(s,fs,mode)
%FXRAPT RAPT pitch tracker [FX,VUV]=(S,FS)
%
% Input:   s(ns)      Speech signal
%          fs         Sample frequency (Hz)
%          mode       'g' will plot a graph [default if no output arguments]
%                     'u' will include unvoiced fames (with fx=NaN)
%
% Outputs: fx(nframe)     Larynx frequency for each fram,e (or NaN for silent/unvoiced)
%          tt(nframe,3)  Start and end samples of each frame. tt(*,3)=1 at the start of each talk spurt
%
% Plots a graph if no outputs are specified showing lag candidates and selected path
%

% Bugs/Suggestions:
%   (1) Include backward DP pass and output the true cost for each candidate.
%   (2) Add an extra state to distinguish between voiceless and silent
%   (3) N-best DP to allow longer term penalties (e.g. for frequent pitch doubling/halving)

% The algorithm is taken from [1] with the following differences:
%
%      (a)  the factor AFACT which in the Talkin algorithm corresponds roughly
%           to the absolute level of harmonic noise in the correlation window. This value
%           is here calculated as the maximum of three figures:
%                   (i) an absolute floor set by PP.rapt_absnoise
%                  (ii) a multiple of the peak signal set by PP.rapt_signoise
%                 (iii) a multiple of the noise floor set by PP.rapt_relnoise
%      (b) The LPC used in calculating the Itakura distance uses a Hamming window rather than
%          a Hanning window.
%
% A C implementation of this algorithm by Derek Lin and David Talkin is included as  "get_f0.c"
% in the esps.zip package available from http://www.speech.kth.se/esps/esps.zip under the BSD
% license.
%
% Refs:
%      [1]   D. Talkin, "A Robust Algorithm for Pitch Tracking (RAPT)"
%            in "Speech Coding & Synthesis", W B Kleijn, K K Paliwal eds,
%            Elsevier ISBN 0444821694, 1995

%      Copyright (C) Mike Brookes 2006
%      Version: $Id: fxrapt.m 3601 2013-10-11 15:27:30Z dmb $
%
%   VOICEBOX is a MATLAB toolbox for speech processing.
%   Home page: http://www.ee.ic.ac.uk/hp/staff/dmb/voicebox/voicebox.html
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%   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 2 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 can obtain a copy of the GNU General Public License from
%   http://www.gnu.org/copyleft/gpl.html or by writing to
%   Free Software Foundation, Inc.,675 Mass Ave, Cambridge, MA 02139, USA.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

s=s(:); % force s to be a column
if nargin<3
    mode=' ';
end
doback=0;   % don't do backwards DP for now

% read in parameters

PP=voicebox;
f0min=PP.rapt_f0min;            % Min F0 (Hz)                               [50]
f0max=PP.rapt_f0max;            % Max F0 (Hz)                               [500]
tframe=PP.rapt_tframe;          % frame size (s)                            [0.01]
tlpw=PP.rapt_tlpw;              % low pass filter window size (s)           [0.005]
tcorw=PP.rapt_tcorw;            % correlation window size (s)               [0.0075]
candtr=PP.rapt_candtr;          % minimum peak in NCCF                      [0.3]
lagwt=PP.rapt_lagwt;            % linear lag taper factor                   [0.3]
freqwt=PP.rapt_freqwt;          % cost factor for F0 change                 [0.02]
vtranc=PP.rapt_vtranc;          % fixed voice-state transition cost         [0.005]
vtrac=PP.rapt_vtrac;            % delta amplitude modulated transition cost [0.5]
vtrsc=PP.rapt_vtrsc;            % delta spectrum modulated transition cost  [0.5]
vobias=PP.rapt_vobias;          % bias to encourage voiced hypotheses       [0.0]
doublec=PP.rapt_doublec;        % cost of exact doubling or halving         [0.35]
absnoise=PP.rapt_absnoise;      % absolute rms noise level                  [0]
relnoise=PP.rapt_relnoise;      % rms noise level relative to noise floor   [2.0]
signoise=PP.rapt_signoise;      % ratio of peak signal rms to noise floor   [0.001]
ncands=PP.rapt_ncands;          % max hypotheses at each frame              [20]
trms=PP.rapt_trms;              % window length for rms measurement         [0.03]
dtrms=PP.rapt_dtrms;            % window spacing for rms measurement        [0.02]
preemph=PP.rapt_preemph;        % s-plane position of preemphasis zero      [-7000]
nfullag=PP.rapt_nfullag;        % number of full lags to try (must be odd)  [7]

% derived parameters (mostly dependent on sample rate fs)

krms=round(trms*fs);            % window length for rms measurement
kdrms=round(dtrms*fs);          % window spacing for rms measurement
rmswin=hanning(krms).^2;
kdsmp=round(0.25*fs/f0max);
hlpw=round(tlpw*fs/2);          % force window to be an odd length
blp=sinc((-hlpw:hlpw)/kdsmp).*hamming(2*hlpw+1).';
fsd=fs/kdsmp;
kframed=round(fsd*tframe);      % downsampled frame length
kframe=kframed*kdsmp;           % frame increment at full rate
rmsix=(1:krms)+floor((kdrms-kframe)/2); % rms index according to Talkin; better=(1:krms)+floor((kdrms-krms+1)/2)
minlag=ceil(fsd/f0max);
maxlag=round(fsd/f0min);        % use round() only because that is what Talkin does
kcorwd=round(fsd*tcorw);        % downsampled correlation window
kcorw=kcorwd*kdsmp;             % full rate correlation window
spoff=max(hlpw-floor(kdsmp/2),1+kdrms-rmsix(1)-kframe);  % offset for first speech frame at full rate
sfoff=spoff-hlpw+floor(kdsmp/2); % offset for downsampling filter
sfi=1:kcorwd;                   % initial decimated correlation window index array
sfhi=1:kcorw;                   % initial correlation window index array
sfj=1:kcorwd+maxlag;
sfmi=repmat((minlag:maxlag)',1,kcorwd)+repmat(sfi,maxlag-minlag+1,1);
lagoff=(minlag-1)*kdsmp;        % lag offset when converting to high sample rate
beta=lagwt*f0min/fs;            % bias towards low lags
log2=log(2);
lpcord=2+round(fs/1000);        % lpc order for itakura distance
hnfullag=floor(nfullag/2);
jumprat=exp((doublec+log2)/2);  % lag ratio at which octave jump cost is lowest
ssq=s.^2;
csssq=cumsum(ssq);
sqrt(min(csssq(kcorw+1:end)-csssq(1:end-kcorw))/kcorw);
afact=max([absnoise^2,max(ssq)*signoise^2,min(csssq(kcorw+1:end)-csssq(1:end-kcorw))*(relnoise/kcorw)^2])^2*kcorw^2;

% downsample signal to approx 2 kHz to speed up autocorrelation calculation
% kdsmp is the downsample factor

sf=filter(blp/sum(blp),1,s(sfoff+1:end));
sp=filter([1 exp(preemph/fs)],1,s); % preemphasised speech for LPC calculation
sf(1:length(blp)-1)=[];         % remove startup transient
sf=sf(1:kdsmp:end);             % downsample to =~2kHz
nsf=length(sf);                 % length of downsampled speech
ns=length(s);                   % length of full rate speech

% Calculate the frame limit to ensure we don't run off the end of the speech or decimated speech:
%   (a) For decimated autocorrelation when calculating sff():  (nframe-1)*kframed+kcorwd+maxlag <= nsf
%   (b) For full rate autocorrelation when calculating sfh():  max(fho)+kcorw+maxlag*kdsamp+hnfllag <= ns
%   (c) For rms ratio window when calculating rr            :  max(fho)+rmsix(end) <= ns
% where max(fho) = (nframe-1)*kframe + spoff

nframe=floor(1+min((nsf-kcorwd-maxlag)/kframed,(ns-spoff-max(kcorw-maxlag*kdsmp-hnfullag,rmsix(end)))/kframe));

% now search for autocorrelation peaks in the downsampled signal

cost=zeros(nframe,ncands);      % cumulative cost
prev=zeros(nframe,ncands);      % traceback pointer
mcands=zeros(nframe,1);         % number of actual candidates excluding voiceless
lagval=repmat(NaN,nframe,ncands-1);    % lag of each voiced candidate
tv=zeros(nframe,3);             % diagnostics: 1=voiceless cost, 2=min voiced cost, 3:cumulative voiceless-min voiced
if doback
    costms=cell(nframe,1);
end

% Main processing loop for each 10 ms frame

for iframe=1:nframe       % loop for each frame (~10 ms)

    % Find peaks in the normalized autocorrelation of subsampled (2Khz) speech
    % only keep peaks that are > 30% of highest peak

    fho=(iframe-1)*kframe+spoff;
    sff=sf((iframe-1)*kframed+sfj);
    sffdc=mean(sff(sfi));       % mean of initial correlation window length
    sff=sff-sffdc;              % subtract off the mean
    nccfd=normxcor(sff(1:kcorwd),sff(minlag+1:end));
    [ipkd,vpkd]=v_findpeaks(nccfd,'q');

    % Debugging: execute the line below to plot the autocorrelation peaks.
    % v_findpeaks(nccfd,'q'); xlabel(sprintf('Lag = (x+%d)*%g ms',minlag-1,1000*kdsmp/fs)); ylabel('Normalized Cross Correlation'); title (sprintf('Frame %d/%d',iframe,nframe));

    vipkd=[vpkd ipkd];
    vipkd(vpkd<max(vpkd)*candtr,:)=[];          % eliminate peaks that are small
    if size(vipkd,1)
        if size(vipkd,1)>ncands-1
            vipkd=sortrows(vipkd);
            vipkd(1:size(vipkd,1)-ncands+1,:)=[];   % eliminate lowest to leave only ncands-1
        end
        lagcan=round(vipkd(:,2)*kdsmp+lagoff);        % convert the lag candidate values to the full sample rate
        nlcan=length(lagcan);
    else
        nlcan=0;
    end

    % If there are any candidate lag values (nlcan>0) then refine their accuracy at the full sample rate

    if nlcan
        laglist=reshape(repmat(lagcan(:)',nfullag,1)+repmat((-hnfullag:hnfullag)',1,nlcan),nfullag*nlcan,1);
        sfh=s(fho+(1:kcorw+max(lagcan)+hnfullag));
        sfhdc=mean(sfh(sfhi));
        sfh=sfh-sfhdc;
        e0=sum(sfh(sfhi).^2);                     % energy of initial correlation window (only needed to store in tv(:,6)
        lagl2=repmat(lagcan(:)',nfullag+kcorw-1,1)+repmat((1-hnfullag:hnfullag+kcorw)',1,nlcan);
        nccf=normxcor(sfh(1:kcorw),sfh(lagl2),afact);

        [maxcc,maxcci]=max(nccf,[],1);
        vipk=[maxcc(:) lagcan(:)+maxcci(:)-hnfullag-1];
        vipk=vipk(:,[1 2 2]);
        maxccj=maxcci(:)'+nfullag*(0:nlcan-1);    % vector index into nccf array
        msk=mod(maxcci,nfullag-1)~=1 & 2*nccf(maxccj)-nccf(mod(maxccj-2,nfullag*nlcan)+1)-nccf(mod(maxccj,nfullag*nlcan)+1)>0;  % don't do quadratic interpolation for the end ones
        if any(msk)
            maxccj=maxccj(msk);
            vipk(msk,3)=vipk(msk,3)+(nccf(maxccj+1)-nccf(maxccj-1))'./(2*(2*nccf(maxccj)-nccf(maxccj-1)-nccf(maxccj+1)))';
        end
        vipk(maxcc<max(maxcc)*candtr,:)=[];          % eliminate peaks that are small
        if size(vipk,1)>ncands-1
            vipk=sortrows(vipk);
            vipk(1:size(vipk,1)-ncands+1,:)=[];   % eliminate lowest to leave only ncands-1
        end

        % vipk(:,1) has NCCF value, vipk(:,2) has integer peak position, vipk(:,3) has refined peak position

        mc=size(vipk,1);
    else
        mc=0;
    end

    % We now have mc lag candidates at the full sample rate

    mc1=mc+1;               % total number of candidates including "unvoiced" possibility
    mcands(iframe)=mc;      % save number of lag candidates (needed for pitch consistency cost calculation)
    if mc
        lagval(iframe,1:mc)=vipk(:,3)';
        cost(iframe,1)=vobias+max(vipk(:,1));   % voiceless cost
        cost(iframe,2:mc1)=1-vipk(:,1)'.*(1-beta*vipk(:,3)');   % local voiced costs
        tv(iframe,2)=min(cost(iframe,2:mc1));
    else
        cost(iframe,1)=vobias;          % if no lag candidates (mc=0), then the voiceless case is the only possibility
    end
    tv(iframe,1)=cost(iframe,1);
    if iframe>1                         % if it is not the first frame, then calculate pitch consistency and v/uv transition costs
        mcp=mcands(iframe-1);
        costm=zeros(mcp+1,mc1);         % cost matrix: rows and cols correspond to candidates in previous and current frames (incl voiceless)

        % if both frames have at least one lag candidate, then calculate a pitch consistency cost

        if mc*mcp
            lrat=abs(log(repmat(lagval(iframe,1:mc),mcp,1)./repmat(lagval(iframe-1,1:mcp)',1,mc)));
            costm(2:end,2:end)=freqwt*min(lrat,doublec+abs(lrat-log2));  % allow pitch doubling/halving
        end

        % if either frame has a lag candidate, then calculate the cost of voiced/voiceless transition and vice versa

        if mc+mcp
            rr=sqrt((rmswin'*s(fho+rmsix).^2)/(rmswin'*s(fho+rmsix-kdrms).^2)); % amplitude "gradient"
            ss=0.2/(distitar(lpcauto(sp(fho+rmsix),lpcord),lpcauto(sp(fho+rmsix-kdrms),lpcord),'e')-0.8);   % Spectral stationarity: note: Talkin uses Hanning instead of Hamming windows for LPC
            costm(1,2:end)= vtranc+vtrsc*ss+vtrac/rr;   % voiceless -> voiced cost
            costm(2:end,1)= vtranc+vtrsc*ss+vtrac*rr;
            tv(iframe,4:5)=[costm(1,mc1) costm(mcp+1,1)];
        end
        costm=costm+repmat(cost(iframe-1,1:mcp+1)',1,mc1);  % add in cumulative costs
        [costi,previ]=min(costm,[],1);
        cost(iframe,1:mc1)=cost(iframe,1:mc1)+costi;
        prev(iframe,1:mc1)=previ;
    else                            % first ever frame
        costm=zeros(1,mc1); % create a cost matrix in case doing a backward recursion
    end
    if mc
        tv(iframe,3)=cost(iframe,1)-min(cost(iframe,2:mc1));
        tv(iframe,6)=5*log10(e0*e0/afact);
    end
    if doback
        costms{iframe}=costm; % need to add repmatted cost into this
    end
end

% now do traceback

best=zeros(nframe,1);
[cbest,best(nframe)]=min(cost(nframe,1:mcands(nframe)+1));
for i=nframe:-1:2
    best(i-1)=prev(i,best(i));
end
vix=find(best>1);
fx=repmat(NaN,nframe,1);                        % unvoiced frames will be NaN
fx(vix)=fs*lagval(vix+nframe*(best(vix)-2)).^(-1); % leave as NaN if unvoiced
tt=zeros(nframe,3);
tt(:,1)=(1:nframe)'*kframe+spoff;       % find frame times
tt(:,2)=tt(:,1)+kframe-1;
jratm=(jumprat+1/jumprat)/2;
tt(2:end,3)=abs(fx(2:end)./fx(1:end-1)-jratm)>jumprat-jratm;    % new spurt if frequency ratio is outside (1/jumprat,jumprat)
tt(1,3)=1;           % first frame always starts a spurt
tt(1+find(isnan(fx(1:end-1))),3)=1; % NaN always forces a new spurt

% plot results if there are no output arguments of if the 'g' mode option is specified

if ~nargout || any(mode=='g')
    tf=spoff+(0:nframe-1)'*kframe;      % one sample before start of each frame
    blag=repmat(NaN,nframe,1);                        % unvoiced frames will be NaN
    blag(vix)=lagval(vix+nframe*(best(vix)-2)); % leave as NaN if unvoiced
    ts=(1:ns)/fs;                       % time scale for speech samples
    tsa=[1:tf(1) tf(end)+kframe+1:ns];  % indexes for unprocessed speech [-1 term is an error methinks]
    sup=repmat(NaN,ns,1);               % unprocessed speech - plot in black
    sup(tsa)=s(tsa);
    sv=reshape(s(tf(1)+1:tf(end)+kframe),kframe,nframe);               % processed speech
    su=sv;
    su(:,best>1)=NaN;                   % delete all voiced samples
    sv(:,best==1)=NaN;                  % delete all unvoiced samples
    tsuv=(tf(1)+1:tf(end)+kframe)/fs;
    su=su(:);
    sv=sv(:);
    ax=zeros(2,1);
    ax(1)=subplot(211);
    plot(ts,sup,'-k',tsuv,su,'r-',tsuv,sv,'b-');
    title('Speech');
    ax(2)=subplot(212);
    plot((tf+(kframe+1)/2)/fs,lagval*1000/fs,'xr',(tf+(kframe+1)/2)/fs,blag*1000/fs,'-b')
    xlabel('Time (s)');
    ylabel('Period (ms)');
    title('Lag Candidates');
    linkaxes(ax,'x');
end
if ~any(mode=='u')
tt(isnan(fx),:)=[];    % remove NaN spurts
fx(isnan(fx),:)=[];
end

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

function v=normxcor(x,y,d)
% Calculate the normalized cross correlation of column vectors x and y
% we can calculate this in two ways but fft is much faster even for nx small
% We must have nx<=ny and the output length is ny-nx+1
% note that this routine does not do mean subtraction even though this is normally a good idea
% if y is a matrix, we correlate with each column
% d is a constant added onto the normalization factor
% v(j)=x'*yj/sqrt(d + x'*x * yj'*yj) where yj=y(j:j+nx-1) for j=1:ny-nx+1

if nargin<3
    d=0;
end
nx=length(x);
[ny,my]=size(y);
nv=1+ny-nx;
if nx>ny
    error('second argument is shorter than the first');
end

nf=pow2(nextpow2(ny));
w=irfft(repmat(conj(rfft(x,nf,1)),1,my).*rfft(y,nf,1));
s=zeros(ny+1,my);
s(2:end,:)=cumsum(y.^2,1);
v=w(1:nv,:)./sqrt(d+(x'*x).*(s(nx+1:end,:)-s(1:end-nx,:)));


function [ly] = ft2ly(ft)
% Convert length from feet to light years.
% Chad A. Greene 2012
ly = ft*3.221738542107e-17;


function [Eft, Covft, ljpyt, Eyt, Covyt] = gpia_jpred(gp_array, x, y, varargin)
%GPIA_JPRED  Prediction with Gaussian Process GP_IA solution.
%
%  Description
%    [EFT, COVFT] = GPIA_JPRED(GP_ARRAY, X, Y, XT, OPTIONS)
%    takes a cell array of GP structures together with matrix X of
%    training inputs and vector Y of training targets, and
%    evaluates the predictive distribution at test inputs XT with
%    parameters marginalized out with IA. Returns a posterior mean
%    EFT and covariance COVFT of latent variables.
%
%    [EFT, COVFT, JPYT, EYT, COVYT] = GPIA_JPRED(GP, X, Y, XT, 'yt', YT, ...)
%    returns also logarithm of the predictive joint density PYT of
%    the observations YT at test input locations XT with parameters
%    marginalized out with IA. This can be used for example in the
%    cross-validation. Here Y has to be vector. Returns also
%    posterior predictive mean EYT and covariance COVYT.
%
%    [EF, COVF, LJPY, EY, COVY] = GPIA_JPRED(GP, X, Y, OPTIONS)
%    evaluates the predictive distribution at training inputs X
%    and logarithm of the joint predictive density JPY of the training
%    observations Y.
%
%    OPTIONS is optional parameter-value pair
%      predcf - index vector telling which covariance functions are
%               used for prediction. Default is all (1:gpcfn). See
%               additional information below.
%      tstind - a vector/cell array defining, which rows of X belong
%               to which training block in *IC type sparse models.
%               Deafult is []. In case of PIC, a cell array
%               containing index vectors specifying the blocking
%               structure for test data. IN FIC and CS+FIC a vector
%               of length n that points out the test inputs that
%               are also in the training set (if none, set TSTIND=[])
%      yt     - optional observed yt in test points (see below)
%      z      - optional observed quantity in triplet (x_i,y_i,z_i)
%               Some likelihoods may use this. For example, in case
%               of Poisson likelihood we have z_i=E_i, that is,
%               expected value for ith case.
%      zt     - optional observed quantity in triplet (xt_i,yt_i,zt_i)
%               Some likelihoods may use this. For example, in case
%               of Poisson likelihood we have z_i=E_i, that is, the
%               expected value for the ith case.
%
%    NOTE! In case of FIC and PIC sparse approximation the
%    prediction for only some PREDCF covariance functions is just
%    an approximation since the covariance functions are coupled in
%    the approximation and are not strictly speaking additive
%    anymore.
%
%    For example, if you use covariance such as K = K1 + K2 your
%    predictions Eft1 = gpia_pred(gp_array, X, Y, X, 'predcf', 1) and
%    Eft2 = gpia_pred(gp_array, x, y, x, 'predcf', 2) should sum up to
%    Eft = gpia_pred(gp_array, x, y, x). That is Eft = Eft1 + Eft2. With
%    FULL model this is true but with FIC and PIC this is true only
%    approximately. That is Eft \approx Eft1 + Eft2.
%
%    With CS+FIC the predictions are exact if the PREDCF covariance
%    functions are all in the FIC part or if they are CS
%    covariances.
%
%    NOTE! When making predictions with a subset of covariance
%    functions with FIC approximation the predictive variance can
%    in some cases be ill-behaved i.e. negative or unrealistically
%    small. This may happen because of the approximative nature of
%    the prediction.
%
%  See also
%    GP_PRED, GP_SET, GP_IA
%
% Copyright (c) 2009 Ville Pietil�inen
% Copyright (c) 2009-2010 Jarno Vanhatalo
% Copyright (c) 2011-2012 Ville Tolvanen
% Copyright (c) 2012 Aki Vehtari

% This software is distributed under the GNU General Public
% Licence (version 3 or later); please refer to the file
% Licence.txt, included with the software, for details.

  ip=inputParser;
  ip.FunctionName = 'GPIA_JPRED';
  ip.addRequired('gp_array', @iscell);
  ip.addRequired('x', @(x) ~isempty(x) && isreal(x) && all(isfinite(x(:))))
  ip.addRequired('y', @(x) ~isempty(x) && isreal(x) && all(isfinite(x(:))))
  ip.addOptional('xt', [], @(x) isempty(x) || (isreal(x) && all(isfinite(x(:)))))
  ip.addParamValue('yt', [], @(x) isreal(x) && all(isfinite(x(:))))
  ip.addParamValue('z', [], @(x) isreal(x) && all(isfinite(x(:))))
  ip.addParamValue('zt', [], @(x) isreal(x) && all(isfinite(x(:))))
  ip.addParamValue('predcf', [], @(x) isempty(x) || ...
                   isvector(x) && isreal(x) && all(isfinite(x)&x>0))
  ip.addParamValue('tstind', [], @(x) isempty(x) || iscell(x) ||...
                   (isvector(x) && isreal(x) && all(isfinite(x)&x>0)))
  if numel(varargin)==0 || isnumeric(varargin{1})
    % inputParser should handle this, but it doesn't
    ip.parse(gp_array, x, y, varargin{:});
  else
    ip.parse(gp_array, x, y, [], varargin{:});
  end
  xt=ip.Results.xt;
  yt=ip.Results.yt;
  z=ip.Results.z;
  zt=ip.Results.zt;
  predcf=ip.Results.predcf;
  tstind=ip.Results.tstind;
  if isempty(xt)
    xt=x;
    if isempty(tstind)
      if iscell(gp_array)
        gptype=gp_array{1}.type;
      else
        gptype=gp_array.type;
      end
      switch gptype
        case {'FULL' 'VAR' 'DTC' 'SOR'}
          tstind = [];
        case {'FIC' 'CS+FIC'}
          tstind = 1:size(x,1);
        case 'PIC'
          if iscell(gp)
            tstind = gp_array{1}.tr_index;
          else
            tstind = gp_array.tr_index;
          end
      end
    end
    if isempty(yt)
      yt=y;
    end
    if isempty(zt)
      zt=z;
    end
  end

  % pass these forward
  options=struct();
  if ~isempty(yt);options.yt=yt;end
  if ~isempty(z);options.z=z;end
  if ~isempty(zt);options.zt=zt;end
  if ~isempty(predcf);options.predcf=predcf;end
  if ~isempty(tstind);options.tstind=tstind;end

  if nargout > 2 && isempty(yt)
    pyt = NaN;
  end

  nGP = numel(gp_array);

  % Make predictions with different models in gp_array
  for i1=1:nGP
    P_TH(:,i1) = gp_array{i1}.ia_weight;
    if nargout <= 2
      [Efts(:,i1), Covfts(:,:,i1)]=gp_jpred(gp_array{i1},x,y,xt,options);
    else
      [Efts(:,i1), Covfts(:,:,i1), ljpyts(i1), Eyts(:,i1), Covyts(:,:,i1)]=gp_jpred(gp_array{i1},x,y,xt, options);
    end
  end

  % Calculate mean and variance of the distributions
  Eft = sum(bsxfun(@times,Efts,P_TH), 2);
  % Calculate covariances of means
  Efts = bsxfun(@minus,Efts,Eft);
  for i1=1:nGP
    CovEfts(:,:,i1)=Efts(:,i1)'*Efts(:,2);
  end
  % Covariance is E(Cov)+Cov(E)
  Covft = squeeze(sum(bsxfun(@times, Covfts, permute(P_TH,[1 3 2])), 3)) ...
          + sum(bsxfun(@times, CovEfts, permute(P_TH,[1 3 2])), 3);

  % Calculate jpyt with weight given in P_TH.
  if nargout > 2
    if isempty(yt)
      ljpyt = [];
    else
      ljpyt = log(sum(exp(ljpyts)'.*P_TH));
    end
  end

  if nargout > 3
    Eyt = sum(bsxfun(@times,Eyts,P_TH),2);
    Covyt = squeeze(sum(bsxfun(@times, Covyts, permute(P_TH,[1 3 2])), 3)) ...
            + diag(sum(bsxfun(@times,bsxfun(@minus,Eyts,Eyt).^2, P_TH),2));
  end


function [MCI] = spm_mci_mfx_dynamic (MCI)
% Mixed Effects Inference for Dynamical Systems
% FORMAT [MCI] = spm_mci_mfx_dynamic (MCI)
%
% MCI               Data structure containing fields:
%
% .M{n}             Model for nth of N replications (e.g. subjects)
% .U{n}             Inputs for nth replication
% .Y{n}             Data for nth replication
% .fixed            Gaussian prior (.pE and .pC) over FFX
% .S                Second level model describing population mean, m, and
%                   precision, Lambda. The parameters in S.prior
%                   define the sufficient statistics of p(Lambda) (.a and .B)
%                   and p(m|Lambda) (.beta and.m)
% .total_its        Total number of samples per subject
% .rinit            Proportion of samples to collect prior to use of
%                   Empirical (group) prior
% .verbose          Show progress of optimisation
%
% KNOWN, FIXED or RANDOM EFFECTS:
% The initial states, flow and output
% parameters can be fixed or random effects or take on known values:
%
% .assign.init_par  'fixed', 'random' or 'known'
% .assign.flow_par  'fixed', 'random' or 'known'
% .assign.out_par   'fixed', 'random' or 'known'
%
% .pinit0           Initial values of initial state parameters
%                   [Ninit x 1] for fixed, [Ninit x N] for random
% .pflow0           Initial values of flow parameters
%                   [Nflow x 1] for fixed, [Nflow x N] for random
% .pout0            Initial values of output parameters
%                   [Nout x 1] for fixed, [Nout x N] for random
%
% .update_obs_noise Update observation noise, Gamma ? [yes/no] (1/0), default=1
% .verbose          Show progress of optimisation
%
% The output fields are:
%
% POSTERIOR SAMPLES:
% .sv               [Nv x Nsamples] group fixed effects samples, v
% .sm               [Nw x Nsamples] group random effect means, m
% .sw               [Nw x N x Nsamples] subject random effects, w
% .Ce               [Ny x Ny x Nsamples] Obs noise covariance samples
% .postind          Indices for posterior (ie. excluding burn-in)
%
% POSTERIOR MEANS:
% .sv_mean          [Nv x 1] posterior mean over v
% .sm_mean          [Nw x 1] posterior mean over m
% .sw_mean          [Nw x N] posterior mean over w
%
% For Dynamical Systems models:
% .pinit            Estimated initial states
% .pflow            Estimated flow
% .pout             Estimated output params
%
% SUFFICIENT STATISTICS:
% .noise            Parameters of p(Gamma|Y,w,v): .c0,.D0,.cN,.DN
% .S.post           Parameters of p(Lambda|w) (.a and.B)
%                   and p(m|Lambda,w) (.beta and .m)
%
% W.Penny, M Klein-Flugge and B Sengupta (2015) Mixed Effects Langevin
% Monte Carlo, Submitted, 2015.
%__________________________________________________________________________
% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging

% Will Penny
% $Id: spm_mci_mfx_dynamic.m 6697 2016-01-27 14:57:28Z spm $

try, verbose=MCI.verbose; catch, verbose=0; end

% Update observation noise ?
try, update_obs_noise=MCI.update_obs_noise; catch, update_obs_noise=1; end

% Numbers of iterations, with and without group model
try, total_its=MCI.total_its; catch, total_its=1024; end
try, rinit=MCI.rinit; catch, rinit=0.25; end
rfx_its=2;
mfx_its=(1-rinit)*total_its/rfx_its;
rfx1_its=rinit*total_its;

try, ffx_its=MCI.ffx_its; catch, ffx_its=4; end
try, ffx1_its=MCI.ffx1_its; catch, ffx1_its=64; end

M=MCI.M;U=MCI.U;Y=MCI.Y;
Np=length(spm_vec(M{1}.pE));
if isfield(M{1},'x0')
    % For dynamical systems
    d=size(M{1}.x0,1);
end

% Observation noise
Ny=size(Y{1}.y,2);
noise.c0=Ny;
noise.D0=eye(Ny);

% Prior over fixed effects
try
    fixed=MCI.fixed;
    fixed.vpE=spm_vec(fixed.pE); % m_v (vector)
catch
    fixed=[];
end

% Prior over random effects (second level model)
try
    S=MCI.S;
    Nrand=S.prior.a;
catch
    if strcmp(MCI.assign.init_par,'random')
        Nrand=d;
    else
        Nrand=0;
    end
    if strcmp(MCI.assign.flow_par,'random')
        Nrand=Nrand+M{1}.Npflow;
    end
    if strcmp(MCI.assign.out_par,'random')
        Nrand=Nrand+M{1}.Npout;
    end
    S.prior.P=Nrand;
    S.prior.a=Nrand/2;
    S.prior.B=eye(Nrand);
    S.prior.beta=1;
    S.prior.m=zeros(Nrand,1);
    S.N=length(M);
end
% Prior mean and cov of random effects from second level model
m=S.prior.m;
C=S.prior.B/S.prior.a;
rfx_prior.pE=m;
rfx_prior.pC=C;
MCI.S=S;

% Initialise fixed and random effects
[w_init,v_init,assign,update_ffx,update_rfx] = spm_mci_vw_init (MCI);

% Sampling params
fixed_mcmc.assign=assign;
subj_mcmc.assign=assign;
subj_mcmc.verbose=0;

% Set up sample matrix
Nfixed = length(v_init);
if Nfixed > 0
    %v = zeros(mfx_its,Nfixed);
    for j=1:rfx1_its,
        v(j,:) = v_init';
    end
end
w = w_init;

% Initialisation of each model
subj_mcmc.verbose=verbose;
subj_mcmc.maxits=rfx1_its;
for n=1:S.N,
    subj_mcmc.init=w(:,n);
    if rfx1_its > 0
        Psamp = spm_mci_random (subj_mcmc,rfx_prior,v_init,M{n},U{n},Y{n});
        sw(n,:,:) = Psamp;
        w(:,n) = Psamp(:,end);
    else
        sw(n,:,1) = w(:,n);
    end
end
sw=permute(sw,[2 1 3]);
sm=squeeze(mean(sw,2));
for j=1:rfx1_its,
    Ce(:,:,j)=M{1}.Ce;
end
subj_mcmc.maxits=rfx_its;
subj_mcmc.verbose=0;

% Main Loop
for it=1:mfx_its

    if verbose
        disp(sprintf('MCI iteration %d',it));
    end

    if it>1 && Nrand > 0
        % Update second level params
        S = spm_nwpost (S,w);
        [m,Lambda,C] = spm_nwrnd (S.post,1);
    end

    if update_ffx
        if verbose, disp('Updating fixed effects'); end
        % 2. Update estimate of group fixed effects

        % Start sampling where left off
        fixed_mcmc.init=v(it,:)';
        fixed_mcmc.update_obs_noise=update_obs_noise;

        if it==1
            fixed_mcmc.verbose=1;
            fixed_mcmc.maxits=ffx1_its;
        else
            fixed_mcmc.maxits=ffx_its;
            fixed_mcmc.verbose=0;
        end
        [Psamp,noise,M] = spm_mci_fixed (fixed_mcmc,w,fixed,noise,M,U,Y);
        P = Psamp(end,:);
        v(rfx1_its+it,:) = P;
        vit=P';
    else
        vit=[];
    end

    if update_rfx
        % Updated prior on random effects
        rfx_prior.pE=m;
        rfx_prior.pC=C;

        % 1. Update estimates of subject random effects
        if verbose, disp('Updating random effects'); end

        for n=1:S.N,
            if verbose
                disp(sprintf('Subject %d out of %d',n,S.N));
            end
            % Start sampling where left off
            subj_mcmc.init=w(:,n);
            Psamp = spm_mci_random (subj_mcmc,rfx_prior,vit,M{n},U{n},Y{n});
            w(:,n) = Psamp(:,end);
        end

    end

    % Update observation noise precision
    if update_obs_noise
        if verbose, disp('Updating observation noise precision'); end
        [noise,M] = spm_mci_obsnoise (w,vit,assign,noise,M,U,Y);
    end
    Ce(:,:,rfx1_its+it)=M{1}.Ce;

    % Store samples
    sm(:,rfx1_its+it)=m;
    sw(:,:,rfx1_its+it)=w;

end

% Define post burn-in
total_its=size(sm,2);
burn_in=round(0.3*total_its);
post_ind=[burn_in+1:total_its];

% Return samples
MCI.sm=sm;
MCI.sw=sw;
try, MCI.sv=v'; catch, MCI.sv=[]; end

% Return posterior means
if update_rfx
    MCI.sm_mean=mean(sm(:,post_ind),2);
    MCI.sw_mean=mean(sw(:,:,post_ind),3);
end
try, MCI.sv_mean=mean(v(post_ind,:)); catch, MCI.sv_mean=[]; end
MCI.post_ind=post_ind;

% Extract posterior means for pinit, pflow, pout
switch assign.init_par,
    case 'fixed',
        MCI.pinit=MCI.sv_mean(:,assign.v_init)';
    case 'random',
        MCI.pinit_sub=MCI.sw_mean(assign.w_init,:);
        MCI.pinit=MCI.sm_mean(assign.w_init,:);
    otherwise
        try, MCI.pinit=MCI.pinit0; catch, MCI.pinit=[]; end
end
switch assign.flow_par,
    case 'fixed',
        MCI.pflow=MCI.sv_mean(:,assign.v_flow)';
    case 'random',
        MCI.pflow_sub=MCI.sw_mean(assign.w_flow,:);
        MCI.pflow=MCI.sm_mean(assign.w_flow,:);
    otherwise
        try, MCI.pflow=MCI.flow0; catch, MCI.pflow=[]; end
end
switch assign.out_par,
    case 'fixed',
        MCI.pout=MCI.sv_mean(:,assign.v_out)';
    case 'random',
        MCI.pout_sub=MCI.sw_mean(assign.w_out,:);
        MCI.pout=MCI.sm_mean(assign.w_out,:);
    otherwise
        try, MCI.pout=MCI.out0; catch, MCI.pout=[]; end
end

MCI.M=M;
MCI.S=S;
MCI.noise=noise;
MCI.assign=assign;
MCI.Ce=Ce;


function [s, cfg] = ft_statfun_indepsamplesF(cfg, dat, design)

% FT_STATFUN_INDEPSAMPLESF calculates the independent samples F-statistic on the
% biological data in dat (the dependent variable), using the information on the
% independent variable (ivar) in design.
%
% Use this function by calling one of the high-level statistics functions as
%   [stat] = ft_timelockstatistics(cfg, timelock1, timelock2, ...)
%   [stat] = ft_freqstatistics(cfg, freq1, freq2, ...)
%   [stat] = ft_sourcestatistics(cfg, source1, source2, ...)
% with the following configuration option
%   cfg.statistic = 'ft_statfun_indepsamplesF'
%
% Configuration options
%   cfg.computestat    = 'yes' or 'no', calculate the statistic (default='yes')
%   cfg.computecritval = 'yes' or 'no', calculate the critical values of the test statistics (default='no')
%   cfg.computeprob    = 'yes' or 'no', calculate the p-values (default='no')
%
% The following options are relevant if cfg.computecritval='yes' and/or
% cfg.computeprob='yes'.
%   cfg.alpha = critical alpha-level of the statistical test (default=0.05)
%   cfg.tail  = -1, 0, or 1, left, two-sided, or right (default=1)
%               cfg.tail in combination with cfg.computecritval='yes'
%               determines whether the critical value is computed at
%               quantile cfg.alpha (with cfg.tail=-1), at quantiles
%               cfg.alpha/2 and (1-cfg.alpha/2) (with cfg.tail=0), or at
%               quantile (1-cfg.alpha) (with cfg.tail=1).
%
% Design specification
%   cfg.ivar  = row number of the design that contains the labels of the conditions that must be
%               compared (default=1). The labels range from 1 to the number of conditions.
%
% See also FT_TIMELOCKSTATISTICS, FT_FREQSTATISTICS or FT_SOURCESTATISTICS

% Copyright (C) 2006, Eric Maris
%
% 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$

% set the defaults
if ~isfield(cfg, 'computestat'),       cfg.computestat='yes';     end
if ~isfield(cfg, 'computecritval'),    cfg.computecritval='no';   end
if ~isfield(cfg, 'computeprob'),       cfg.computeprob='no';      end
if ~isfield(cfg, 'alpha'),             cfg.alpha=0.05;            end
if ~isfield(cfg, 'tail'),              cfg.tail=1;                end

% perform some checks on the configuration
if strcmp(cfg.computeprob,'yes') && strcmp(cfg.computestat,'no')
    ft_error('P-values can only be calculated if the test statistics are calculated.');
end
if isfield(cfg,'uvar') && ~isempty(cfg.uvar)
    ft_error('cfg.uvar should not exist for an independent samples statistic');
end

ncond=length(unique(design(cfg.ivar,:)));
nrepl=0;
for condindx=1:ncond
    nrepl=nrepl+length(find(design(cfg.ivar,:)==condindx));
end
if nrepl<size(design,2)
  ft_error('Invalid specification of the independent variable in the design array.');
end
if nrepl<=ncond
    ft_error('The must be more trials/subjects than levels of the independent variable.');
end
dfnum = ncond - 1;
dfdenom = nrepl - ncond;

nsmpls = size(dat,1);

if strcmp(cfg.computestat, 'yes')
  % compute the statistic
  nobspercell = zeros(1,ncond);
  avgs = zeros(nsmpls,ncond);
  pooledvar = zeros(nsmpls,1);
  for condindx=1:ncond
      sel=find(design(cfg.ivar,:)==condindx);
      nobspercell(condindx)=length(sel);
      avgs(:,condindx)=mean(dat(:,sel),2);
      pooledvar = pooledvar + nobspercell(condindx)*var(dat(:,sel),1,2);
  end
  pooledvar = pooledvar/dfdenom;
  globalavg = mean(dat,2);
  mseffect = ((avgs-repmat(globalavg,1,ncond)).^2)*nobspercell'./dfnum;
  s.stat = mseffect./pooledvar;
end

if strcmp(cfg.computecritval,'yes')
  % also compute the critical values
  s.dfnum   = dfnum;
  s.dfdenom = dfdenom;
  if cfg.tail==-1
      ft_error('For an independent samples F-statistic, it does not make sense to calculate a left tail critical value.');
  end
  if cfg.tail==0
      ft_error('For an independent samples F-statistic, it does not make sense to calculate a two-sided critical value.');
  end
  if cfg.tail==1
    s.critval = finv(1-cfg.alpha,s.dfnum,s.dfdenom);
  end
end

if strcmp(cfg.computeprob,'yes')
  % also compute the p-values
  s.dfnum   = dfnum;
  s.dfdenom = dfdenom;
  if cfg.tail==-1
      ft_error('For an independent samples F-statistic, it does not make sense to calculate a left tail p-value.');
  end
  if cfg.tail==0
      ft_error('For an independent samples F-statistic, it does not make sense to calculate a two-sided p-value.');
  end
  if cfg.tail==1
    s.prob = 1-fcdf(s.stat,s.dfnum,s.dfdenom);
  end
end


%%*****************************************************************
%% HKMrhsfun: compute the right-hand side vector of the
%%            Schur complement equation for the  HKM direction.
%%*****************************************************************
%% SDPT3: version 4.0
%% Copyright (c) 1997 by
%% Kim-Chuan Toh, Michael J. Todd, Reha H. Tutuncu
%% Last Modified: 16 Sep 2004
%%*****************************************************************

  function [rhs,EinvRc,hRd] = HKMrhsfun(blk,At,par,X,Z,rp,Rd,sigmu,hRd,dX,dZ)

    m = length(rp);
    if (nargin > 8)
       corrector = 1;
    else
       corrector = 0;
       hRd = zeros(m,1);
    end
    hEinvRc = zeros(m,1);
    EinvRc = cell(size(blk,1),1);
    rhsfree = [];
%%
    for p = 1:size(blk,1)
       pblk = blk(p,:);
       n = sum(pblk{2});
       if strcmp(pblk{1},'l')
	  if iscell(sigmu)
             EinvRc{p} = sigmu{p}./Z{p} -X{p};
	  else
             EinvRc{p} = sigmu./Z{p} -X{p};
          end
          Rq = sparse(n,1);
          if (corrector) & (norm(par.parbarrier{p})==0)
             Rq = dX{p}.*dZ{p}./Z{p};
          else
             tmp  = par.dd{p}.*Rd{p};
             tmp2 = mexMatvec(At{p},tmp,1);
             hRd = hRd + tmp2;
          end
	  EinvRc{p} = EinvRc{p} - Rq;
          tmp2 = mexMatvec(At{p,1},EinvRc{p},1);
          hEinvRc = hEinvRc + tmp2;
       elseif strcmp(pblk{1},'q')
	  if iscell(sigmu)
             EinvRc{p} = qops(pblk,sigmu{p},par.Zinv{p},3) -X{p};
	  else
             EinvRc{p} = sigmu*par.Zinv{p} -X{p};
          end
          Rq = sparse(n,1);
          if (corrector) & (norm(par.parbarrier{p})==0)
  	     ff{p} = qops(pblk,1./par.gamz{p},Z{p},3);
             hdx = qops(pblk,par.gamz{p},ff{p},5,dX{p});
             hdz = qops(pblk,par.gamz{p},ff{p},6,dZ{p});
             hdxdz = Arrow(pblk,hdx,hdz);
             Rq = qops(pblk,par.gamz{p},ff{p},6,hdxdz);
          else
             tmp = par.dd{p}.*Rd{p} ...
                   + qops(pblk,qops(pblk,Rd{p},par.Zinv{p},1),X{p},3) ...
                   + qops(pblk,qops(pblk,Rd{p},X{p},1),par.Zinv{p},3);
             tmp2 = mexMatvec(At{p,1},tmp,1);
             hRd = hRd + tmp2;
          end
	  EinvRc{p} = EinvRc{p} - Rq;
          tmp2 = mexMatvec(At{p,1},EinvRc{p},1);
          hEinvRc = hEinvRc + tmp2;
       elseif strcmp(pblk{1},'s')
	  if iscell(sigmu)
             %%ss = [0,cumsum(pblk{2})];
             %%sigmuvec = zeros(n,1);
             %%for k = 1:length(pblk{2});
             %%   sigmuvec(ss(k)+1:ss(k+1)) = sigmu{p}(k)*ones(pblk{2}(k),1);
             %%end
             sigmuvec = mexexpand(pblk{2},sigmu{p});
             EinvRc{p} = par.Zinv{p}*spdiags(sigmuvec,0,n,n) -X{p};
          else
             EinvRc{p} = sigmu*par.Zinv{p} -X{p};
          end
          Rq = sparse(n,n);
          if (corrector) & (norm(par.parbarrier{p})==0)
             Rq = Prod3(pblk,dX{p},dZ{p},par.Zinv{p},0);
	     Rq = 0.5*(Rq+Rq');
          else
             tmp = Prod3(pblk,X{p},Rd{p},par.Zinv{p},0,par.nzlistAy{p});
             tmp = 0.5*(tmp+tmp');
             tmp2 = AXfun(pblk,At(p,:),par.permA(p,:),{tmp});
             hRd = hRd + tmp2;
          end
	  EinvRc{p} = EinvRc{p} - Rq;
          tmp2 = AXfun(pblk,At(p,:),par.permA(p,:),EinvRc(p));
          hEinvRc = hEinvRc + tmp2;
       elseif strcmp(pblk{1},'u')
          rhsfree = [rhsfree; Rd{p}];
       end
    end
%%
    rhs = rp + hRd - hEinvRc;
    rhs = full([rhs; rhsfree]);
%%*******************************************************************