function [vert,conn,tria,tnum] = refine2_om(varargin)
%REFINE2_OM (Frontal)-Delaunay-refinement for two-dimensional,
% polygonal geometries.
% Has been slightly edited for OceanMesh2D to make sure boundary edges are
% absolutely preserved and to get rid of non-unique points
%   [VERT,EDGE,TRIA,TNUM] = REFINE2(NODE,EDGE) returns a co-
%   nstrained Delaunay triangulation of the polygonal region
%   {NODE,EDGE}. NODE is an N-by-2 array of polygonal verti-
%   ces and EDGE is an E-by-2 array of edge indexing. Each
%   row in EDGE represents an edge of the polygon, such that
%   NODE(EDGE(JJ,1),:) and NODE(EDGE(JJ,2),:) are the coord-
%   inates of the endpoints of the JJ-TH edge. If the argum-
%   ent EDGE is omitted it assumed that the vertices in NODE
%   are connected in ascending order.
%
%   [...] = REFINE2(NODE,EDGE,PART) computes a triangulation
%   for a multiply-connected geometry. PART is a cell-array
%   of polygonal "parts", where each element PART{KK} is an
%   array of edge indices defining a given polygonal region.
%   EDGE(PART{KK}, :) is the set of edges in the KK-TH part.
%
%   VERT is a V-by-2 array of XY coordinates in the triangu-
%   lation, EDGE is an array of constrained edges, TRIA is a
%   T-by-3 array of triangles, and TNUM is a T-by-1 array of
%   part indices. Each row of TRIA and EDGE define an eleme-
%   nt. VERT(TRIA(II,1),:), VERT(TRIA(II,2),:) and VERT(TRIA
%   (II,3),:) are the coordinates of the II-TH triangle. The
%   edges in EDGE are defined in a similar manner. NUM is an
%   array of part indexing, such that TNUM(II) is the index
%   of the part in which the II-TH triangle resides.
%
%   [...] = REFINE2(..., OPTS) passes an additional options
%   structure OPTS, containing various user-defined paramet-
%   ers, including:
%
% - OPTS.KIND = {'DELFRONT'}, 'DELAUNAY' -- the type of ref-
%   inement employed. The 'DELFRONT' algorithm is typically
%   slower, but produces higher quality output.
%
% - OPTS.RHO2 = {1.025} -- the maximum allowable radius-edge
%   ratio. Refinement proceeds until all interior triangles
%   satisfy the radius-edge threshold. Smaller radius-edge
%   ratios lead to improved triangle shape, with RHO2=1 req-
%   uiring that all angles exceed 30 degrees. Setting RHO2<1
%   may lead to non-convergence.
%
% - OPTS.REF1 = {'REFINE'}, 'PRESERVE' -- refinement 'flag'
%   for 1-dimensional faces (i.e. edges). The 'PRESERVE' op-
%   tion results in minimal refinement, attempting to retain
%   the initial edges without further subdivision. Edges are
%   split only to satisfy basic geomertical conformance.
%
% - OPTS.REF2 = {'REFINE'}, 'PRESERVE' -- refinement 'flag'
%   for 2-dimensional faces (i.e. trias). The 'PRESERVE' op-
%   tion results in minimal refinement, attempting to retain
%   the initial trias without further subdivision. Trias are
%   split only to satisfy basic geomertical conformance.
%
% - OPTS.SIZ1 = {1.333} -- the normalised rel.-length th-
%   reshold for edge-elements. Each exterior edge is refined
%   until LL/HH<SIZ1, where LL is the edge-length, HH is the
%   edge-centred mesh-size value.
%
% - OPTS.SIZ2 = {1.300} -- the normalised rel.-length th-
%   reshold for tria-elements. Each interior tria is refined
%   until RE/HH<SIZ2, where RE is an effective tria length,
%   based on the circumradius, HH is the tria-centred mesh-
%   size value.
%
% - OPTS.DISP = { +10 } -- refinement verbosity. Set to INF
%   for quiet execution.
%
%   [...] = REFINE2(..., HFUN,ARGS) also passes an optional
%   mesh-size function argument. Setting HFUN = HMAX, where
%   HMAX is a scalar value, imposes a constant size constra-
%   int over the full domain. HFUN can also be defined as a
%   general function handle [HH] = HFUN(PP), where PP is an
%   N-by-2 array of XY coordinates and HH is the associated
%   vector of mesh-size values. User-defined HFUN must be
%   fully vectorised. Additional arguments {A1,A2,...AN} for
%   HFUN can be passed as trailing parameters to REFINE2. In
%   such cases, HFUN must adopt a signature [HH] = HFUN(PP,
%   A1,A2,...,AN). HFUN must return positive values.
%
%   See also SMOOTH2, TRIDIV2, TRICOST, TRIDEMO

%   This routine implements a "multi-refinement" variant of
%   Delaunay-refinement type mesh-generation. Both standard
%   Delaunay-refinement and Frontal-Delaunay type algorithms
%   are available. The Frontal-Delaunay approach is a simpl-
%   ified version of the JIGSAW algorithm, described in:
%
% * D. Engwirda, (2014): "Locally-optimal Delaunay-refineme-
%   nt and optimisation-based mesh generation", Ph.D. Thesis
%   School of Mathematics and Statistics, Univ. of Sydney.
%   http://hdl.handle.net/2123/13148
%
% * D. Engwirda & D. Ivers, (2016): "Off-centre Steiner poi-
%   nts for Delaunay-refinement on curved surfaces", Comput-
%   er-Aided Design, (72), 157--171.
%   http://dx.doi.org/10.1016/j.cad.2015.10.007

%   This work is an extension of the "off-centre" type tech-
%   niques introduced in:
%
% * H. Erten & A. Ungor, (2009): "Quality triangulation with
%   locally optimal Steiner points", SIAM Journal on Scient-
%   ific Comp. 31(3), 2103--2130.
%   http://doi.org/10.1137/080716748
%
% * S. Rebay, (1993): "Efficient Unstructured Mesh Generati-
%   on by Means of Delaunay Triangulation and Bowyer-Watson
%   Algorithm, J. Comp. Physics 106(1), 125--138.
%   http://dx.doi.org/10.1006/jcph.1993.1097

%   Generally speaking, the Delaunay-refinement method impl-
%   emented here is a variantion of the "classical" algorit-
%   hm introduced in:
%
% * J. Ruppert, (1995): "A Delaunay refinement algorithm for
%   quality 2-dimensional mesh generation." Journal of Algo-
%   rithms 18(3), 548--585.
%   http://dx.doi.org/10.1006/jagm.1995.1021
%
%   See also: S. Cheng, T. Dey & J. Shewchuk, (2012): "Dela-
%   unay mesh generation", CRC Press, for comprehensive cov-
%   erage of Delaunay-based meshing techniques.

%   A much more advanced, and fully three-dimensional imple-
%   mentation is available in the JIGSAW library. For addit-
%   ional information, see:
%   https://github.com/dengwirda/jigsaw-matlab

%-----------------------------------------------------------
%   Darren Engwirda : 2017 --
%   Email           : de2363@columbia.edu
%   Last updated    : 09/07/2018
%-----------------------------------------------------------

    node = []; PSLG = []; part = {}; opts = [] ;
    hfun = []; harg = {};

%---------------------------------------------- extract args
    if (nargin>=+1), node = varargin{1}; end
    if (nargin>=+2), PSLG = varargin{2}; end
    if (nargin>=+3), part = varargin{3}; end
    if (nargin>=+4), opts = varargin{4}; end
    if (nargin>=+5), hfun = varargin{5}; end
    if (nargin>=+6), harg = varargin(6:end); end

   [opts] = makeopt(opts) ;

%---------------------------------------------- default EDGE
    nnod = size(node,1) ;

    if (isempty(PSLG))
        PSLG = [(1:nnod-1)',(2:nnod)'; nnod,1] ;
    end

%---------------------------------------------- default PART
    ncon = size(PSLG,1) ;

    if (isempty(part)), part{1} = (1:ncon)'; end

%---------------------------------------------- basic checks
    if (~isnumeric(node) || ~isnumeric(PSLG) || ...
        ~iscell   (part) || ~isstruct (opts) )
        error('refine2:incorrectInputClass' , ...
            'Incorrect input class.') ;
    end

%---------------------------------------------- basic checks
    if (ndims(node) ~= +2 || ndims(PSLG) ~= +2)
        error('refine2:incorrectDimensions' , ...
            'Incorrect input dimensions.');
    end
    if (size(node,2) < +2 || size(PSLG,2) < +2)
        error('refine2:incorrectDimensions' , ...
            'Incorrect input dimensions.');
    end

%---------------------------------------------- basic checks
    if (min([PSLG(:)])<+1 || max([PSLG(:)])>nnod)
        error('refine2:invalidInputs', ...
            'Invalid EDGE input array.') ;
    end

    pmin = cellfun(@min,part);
    pmax = cellfun(@max,part);

    if (min([pmin(:)])<+1 || max([pmax(:)])>ncon)
        error('refine2:invalidInputs', ...
            'Invalid PART input array.') ;
    end

%-------------------------------- prune any non-unique topo.
   [~,ivec,jvec] = ...
        unique(sort(PSLG,+2),'rows') ;

    PSLG = PSLG(ivec,:) ;

    for ppos = +1:length(part)

        if ( ~isnumeric(part{ppos}) )
            error (  ...
            'refine2:incorrectInputClass', ...
                'Incorrect input class. ') ;
        end

        part{ppos} = ...
            unique(jvec(part{ppos})) ;

    end

%-------------------------------- check part "manifold-ness"
    for ppos = +1:length(part)

        eloc = PSLG(part{ppos},:) ;
        nadj = ...
            accumarray(eloc(:),1) ;

        if (any(mod(nadj,2) ~= 0) )
        error('refine2:nonmanifoldInputs', ...
            'Non-manifold PART detected.') ;
        end

    end

%---------------------------------------------- output title
    if (~isinf(opts.disp))
        fprintf(1,'\n') ;
        fprintf(1,' Refine triangulation...\n') ;
        fprintf(1,'\n') ;
        fprintf(1,[...
' -------------------------------------------------------\n', ...
'      |ITER.|          |CDT1(X)|          |CDT2(X)|     \n', ...
' -------------------------------------------------------\n', ...
             ] ) ;
    end

%-------------------------------- PASS 0: inflate box bounds
    vert = node; tria = []; tnum = []; iter = 0 ;
    conn = PSLG;

    vmin = min(vert,[],1);      % inflate bbox for stability
    vmax = max(vert,[],1);

    vdel = vmax - 1.*vmin;
    vmin = vmin - .5*vdel;
    vmax = vmax + .5*vdel;

    vbox = [
        vmin(1), vmin(2)
        vmax(1), vmin(2)
        vmax(1), vmax(2)
        vmin(1), vmax(2)
           ] ;
    vert = [vert ; vbox] ;

%-------------------------------- PASS 0: shield sharp feat.
   [vert,conn,tria,tnum,iter] = ...
        cdtbal0(vert,conn,tria,tnum, ...
            node,PSLG,part,opts,hfun,harg,iter);

%-------------------------------- PASS 1: refine 1-simplexes
   [vert,conn,tria,tnum,iter] = ...
        cdtref1(vert,conn,tria,tnum, ...
            node,PSLG,part,opts,hfun,harg,iter);

%-------------------------------- PASS 2: refine 2-simplexes
   [vert,conn,tria,tnum,iter] = ...
        cdtref2(vert,conn,tria,tnum, ...
            node,PSLG,part,opts,hfun,harg,iter);

    if (~isinf(opts.disp)), fprintf(1,'\n'); end

%-------------------------------- trim extra adjacency info.
    tria = tria( :,1:3) ;

%-------------------------------- trim vert. - deflate bbox.
    keep = false(size(vert,1),1);
    keep(tria(:)) = true;
    keep(conn(:)) = true;

    redo = zeros(size(vert,1),1);
    redo(keep) = ...
        (+1:length(find(keep)))';

    conn = redo(conn);
    tria = redo(tria);

    vert = vert(keep,:) ;

end

function [vert,conn,tria,tnum,iter] = ...
            cdtbal0(vert,conn,tria,tnum, ...
                node,PSLG,part,opts,hfun,harg,iter)
%CDTBAL0 constrained Delaunay-refinement for "sharp" 0-dim.
%features at PSLG vertices.
%   [...] = CDTBAL0(...) refines the set of 1-simplex eleme-
%   nts incident to "sharp" features in the PSLG. Specifica-
%   lly, edges that subtend "small" angles are split about a
%   set of new "collar" vertices, equi-distributed about the
%   centre of "sharp" features. Collar size is computed as a
%   min. of the incident edge-len. and local mesh-size cons-
%   traints.

    if (iter <= opts.iter)

    %------------------------------------- build current CDT
       [vert,conn, ...
        tria,tnum] = deltri2(vert,conn, ...
                             node,PSLG, ...
                             part, ...
                             opts.dtri) ;

    %------------------------------------- build current adj
       [edge,tria] = tricon2(tria,conn) ;

       [feat,ftri] = isfeat2(vert, ...
                             edge,tria) ;

        apex = false(size(vert,1), 1) ;
        apex(tria(ftri)) =  true ;

        if strcmpi(opts.ref1,'preserve')
            return;
        end

    %------------------------------------- eval. length-fun.
        if (~isempty(hfun))
            if (isnumeric(hfun))
            vlen = hfun * ...
              ones(size(vert,1),1) ;
            else
            vlen = feval( ...
                hfun,vert,harg{:}) ;
            vlen = vlen(:) ;
            end
        else
            vlen = +inf * ...
              ones(size(vert,1),1) ;
        end

    %------------------------------------- form edge vectors
        evec = vert(conn(:,2),:) ...
             - vert(conn(:,1),:) ;
        elen = sqrt(sum(evec.^2,2));
        evec = evec./[elen,elen] ;

    %------------------------------------- min. adj. lengths
        for epos = +1 : size(conn,1)

            ivrt = conn(epos,1) ;
            jvrt = conn(epos,2) ;

            vlen(ivrt) = min( ...
            vlen(ivrt), .67*elen(epos)) ;
            vlen(jvrt) = min( ...
            vlen(jvrt), .67*elen(epos)) ;

        end

    %------------------------------------- mark feature edge
        iref = apex(conn(:,1)) ...      %- refine at vert. 1
            & ~apex(conn(:,2)) ;
        jref = apex(conn(:,2)) ...      %- refine at vert. 2
            & ~apex(conn(:,1)) ;
        dref = apex(conn(:,1)) ...      %- refine at both!
            &  apex(conn(:,2)) ;

        keep =~apex(conn(:,1)) ...      %- refine at neither
            & ~apex(conn(:,2)) ;

    %------------------------------------- protecting collar
        ilen = vlen(conn(iref,1)) ;
        inew = vert(conn(iref,1),:) ...
        + [ilen,ilen].*evec(iref,:) ;

        jlen = vlen(conn(jref,2)) ;
        jnew = vert(conn(jref,2),:) ...
        - [jlen,jlen].*evec(jref,:) ;

        Ilen = vlen(conn(dref,1)) ;
        Inew = vert(conn(dref,1),:) ...
        + [Ilen,Ilen].*evec(dref,:) ;

        Jlen = vlen(conn(dref,2)) ;
        Jnew = vert(conn(dref,2),:) ...
        - [Jlen,Jlen].*evec(dref,:) ;

        vnew = [inew; jnew; Inew; Jnew] ;

    %------------------------------------- add new vert/edge
        iset = (1:size(inew,1))' ...
                + size(vert,1) ;

        jset = (1:size(jnew,1))' ...
                + size(inew,1) + ...
                + size(vert,1) ;

        Iset = (1:size(Inew,1))' ...
                + size(inew,1) + ...
                + size(jnew,1) + ...
                + size(vert,1) ;

        Jset = (1:size(Jnew,1))' ...
                + size(inew,1) + ...
                + size(jnew,1) + ...
                + size(Inew,1) + ...
                + size(vert,1) ;

        vert = [vert ; vnew] ;

        cnew = [conn(iref,1), iset ;
                conn(iref,2), iset ;
                conn(jref,2), jset ;
                conn(jref,1), jset ;
                conn(dref,1), Iset ;
                conn(dref,2), Jset ;
                Iset, Jset] ;
        conn = [conn(keep,:); cnew ] ;

    end

end

function [vert,conn,tria,tnum,iter] = ...
            cdtref1(vert,conn,tria,tnum, ...
                node,PSLG,part,opts,hfun,harg,iter)
%CDTREF1 constrained Delaunay-refinement for 1-simplex elem-
%nts embedded in R^2.
%   [...] = CDTREF1(...) refines the set of 1-simplex eleme-
%   nts embedded in the triangulation until all constraints
%   are satisfied. Specifically, edges are refined until all
%   local mesh-spacing and encroachment conditions are met.
%   Refinement proceeds according to either a Delaunay-refi-
%   nement or Frontal-Delaunay type approach, depending on
%   user-settings. In either case, new steiner vertices are
%   introduced to split "bad" edges - those that violate the
%   set of prescribed constraints. In the "-DR" type process
%   edges are split about their circumballs (midpoints). In
%   the "-FD" approach, new vertices are positioned such th-
%   at mesh-spacing constraints are satisfied in a "locally-
%   optimal" fashion.

    tcpu.full = +0. ;
    tcpu.ball = +0. ;
    tcpu.hfun = +0. ;
    tcpu.encr = +0. ;
    tcpu.offc = +0. ;

    vidx = (1:size(vert,1))';     %- "new" vert list to test

    tnow =  tic ;

    ntol = +1.55;

    while (strcmpi(opts.ref1,'refine'))

        iter = iter + 1 ;

        if (iter>=opts.iter),break; end

    %------------------------------------- calc. circumballs
        ttic = tic ;

        bal1 = cdtbal1(vert,conn) ;

        tcpu.ball = ...
            tcpu.ball + toc(ttic) ;

    %------------------------------------- eval. length-fun.
        ttic = tic ;

        if (~isempty(hfun))
            if (isnumeric(hfun))
            fun0 = hfun * ...
              ones(size(vert,1),1);
            fun1 = hfun ;
            else
            fun0(vidx) = ...
                feval(hfun, ...
            vert(vidx,:), harg{:});
            fun0 = fun0(:) ;
            fun1 = fun0(conn(:,1))...
                 + fun0(conn(:,2));
            fun1 = fun1 / +2. ;
            end
        else
            fun0 = +inf * ...
              ones(size(vert,1),1);
            fun1 = +inf ;
        end

        siz1 = ...
         +4. * bal1(:,3)./(fun1.*fun1) ;

        tcpu.hfun = ...
            tcpu.hfun + toc(ttic) ;

    %------------------------------------- test encroachment
        ttic = tic ;

        bal1(:,3) = ...
            (1.-eps^.75) * bal1(:,3) ;

       [vp,vi] = ...
           findball(bal1,vert(:,1:2));

    %------------------------------------- near=>[vert,edge]
        next = +0;
        ebad = false(size(conn,1),1) ;
        near = zeros(size(conn,1),1) ;
        for ii = +1 : size(vp,1)
            for ip = vp(ii,1):vp(ii,2)
                jj = vi(ip);
                if (ii ~= conn(jj,1) ...
                &&  ii ~= conn(jj,2) )
                next = next + 1;
                near(next,1) = ii;
                near(next,2) = jj;
                end
            end
        end

        near = near(1:next-0,:);

        if (~isempty(near))
    %-- mark edge "encroached" if there is a vert within its
    %-- dia.-ball that is not joined to either of its vert's
    %-- via an existing edge...
            ivrt = conn(near(:,2),1);
            jvrt = conn(near(:,2),2);

            pair = [near(:,1), ivrt];
            ivec = setset2(pair,conn) ;

            pair = [near(:,1), jvrt];
            jvec = setset2(pair,conn) ;

            okay = ~ivec & ~jvec ;

            ebad(near(okay,2))=true ;

        end

        tcpu.encr = ...
            tcpu.encr + toc(ttic);

    %------------------------------------- refinement queues
        ref1 = false(size(conn,1),1);
        ref1(ebad)           = true ;   %- edge encroachment
        ref1(siz1>opts.siz1* ...        %- bad equiv. length
                  opts.siz1) = true ;

        num1 = find(ref1)  ;

    %------------------------------------- dump-out progess!
        if (mod(iter,opts.disp)==0)
            numc = size(conn,1) ;
            numt = size(tria,1) ;
            fprintf(+1, ...
            '%11i %18i %18i\n', ...
            [iter,numc,numt]) ;
        end

    %------------------------------------- nothing to refine
        if (isempty(num1)), break; end

    %------------------------------------- refine "bad" tria
        switch (lower(opts.kind))
        case 'delaunay'
    %------------------------------------- do circ-ball pt's
        new1 = bal1(ref1, 1:2) ;

        vidx = (1:size(new1,1))' ...
                + size(vert,1) ;

        cnew = [conn( ref1,1), vidx
                conn( ref1,2), vidx];
        conn = [conn(~ref1,:); cnew];

    %------------------------------------- update vertex set
        vert = [vert; new1(:,1:2)];

        case 'delfront'
    %-- symmetric off-centre scheme:- refine edges from both
    %-- ends simultaneously, placing new vertices to satisfy
    %-- the worst of mesh-spacing and local voronoi constra-
    %-- ints.

        ttic = tic ;

        evec = vert(conn(ref1,2),:) ...
             - vert(conn(ref1,1),:) ;
        elen = sqrt(sum(evec.^2,2)) ;
        evec = evec ./ [elen, elen] ;

    %------------------------------------- "voro"-type dist.
        vlen = sqrt(bal1(ref1,3));

    %------------------------------------- "size"-type dist.
        ihfn = fun0(conn(ref1,1));
        jhfn = fun0(conn(ref1,2));

    %------------------------------------- bind "safe" dist.
        ilen = min(vlen,ihfn) ;
        jlen = min(vlen,jhfn) ;

    %------------------------------------- locate offcentres
        inew = vert(conn(ref1,1),:) ...
             + [ilen,ilen].*evec ;
        jnew = vert(conn(ref1,2),:) ...
             - [jlen,jlen].*evec ;

    %------------------------------------- iter. "size"-type
        for ioff = +1 : +3
    %------------------------------------- eval. length-fun.
        if (~isempty(hfun))
            if (isnumeric(hfun))
            iprj = hfun * ...
              ones(size(inew,1),1);
            jprj = hfun * ...
              ones(size(jnew,1),1);
            else
            iprj = feval( ...
                hfun,inew,harg{:});
            jprj = feval( ...
                hfun,jnew,harg{:});
            iprj = iprj(:);
            jprj = jprj(:);
            end
        else
            iprj = +inf * ...
              ones(size(inew,1),1);
            jprj = +inf * ...
              ones(size(jnew,1),1);
        end

        iprj = 0.5*ihfn + 0.5*iprj;
        jprj = 0.5*jhfn + 0.5*jprj;

    %------------------------------------- bind "safe" dist.
        ilen = min(vlen,iprj) ;
        jlen = min(vlen,jprj) ;

    %------------------------------------- locate offcentres
        inew = vert(conn(ref1,1),:) ...
             + [ilen,ilen].*evec ;
        jnew = vert(conn(ref1,2),:) ...
             - [jlen,jlen].*evec ;

        end

    %------------------------------------- merge i,j if near
        near = ...
            ilen+jlen>=vlen*ntol ;

        znew = inew(near,:) * .5 ...
             + jnew(near,:) * .5 ;

        inew = inew(~near,1:2) ;
        jnew = jnew(~near,1:2) ;

    %------------------------------------- split constraints
        zset = (1:size(znew,1))' ...
                + size(vert,1) ;

        iset = (1:size(inew,1))' ...
                + size(znew,1) + ...
                + size(vert,1) ;

        jset = (1:size(jnew,1))' ...
                + size(znew,1) + ...
                + size(inew,1) + ...
                + size(vert,1) ;

        set1 = num1( near);
        set2 = num1(~near);

        cnew = [conn( set1,1), zset
                conn( set1,2), zset
                conn( set2,1), iset
                conn( set2,2), jset
                iset, jset ] ;
        conn = [conn(~ref1,:); cnew];

    %------------------------------------- update vertex set
        vert = [vert; znew(:,1:2)];
        vert = [vert; inew(:,1:2)];
        vert = [vert; jnew(:,1:2)];

        vidx = [zset; iset; jset] ;

        tcpu.offc = ...
            tcpu.offc + toc(ttic) ;

        end % switch(lower(opts.kind))

    end

    tcpu.full = ...
        tcpu.full + toc(tnow) ;

    if (~isinf(opts.disp) )
    %------------------------------------- print final stats
        numc = size(conn,1) ;
        numt = size(tria,1) ;
        fprintf(+1, ...
        '%11i %18i %18i\n', ...
        [iter,numc,numt]) ;
    end

    if (opts.dbug)
    %------------------------------------- print debug timer
        fprintf(1,'\n') ;
        fprintf(1,' 1-simplex REF. timer...\n');
        fprintf(1,'\n') ;
        fprintf(1, ...
        ' FULL: %f \n', tcpu.full);
        fprintf(1, ...
        ' BALL: %f \n', tcpu.ball);
        fprintf(1, ...
        ' HFUN: %f \n', tcpu.hfun);
        fprintf(1, ...
        ' ENCR: %f \n', tcpu.encr);
        fprintf(1, ...
        ' OFFC: %f \n', tcpu.offc);
        fprintf(1,'\n') ;
    end

end

function [vert,conn,tria,tnum,iter] = ...
            cdtref2(vert,conn,tria,tnum, ...
                node,PSLG,part,opts,hfun,harg,iter)
%CDTREF2 constrained Delaunay-refinement for 2-simplex elem-
%nts embedded in R^2.
%   [...] = CDTREF2(...) refines the set of 2-simplex eleme-
%   nts embedded in the triangulation until all constraints
%   are satisfied. Specifically, triangles are refined until
%   all local mesh-spacing and element-shape conditions are
%   met. Refinement proceeds according to either a Delaunay-
%   refinement or Frontal-Delaunay type approach, depending
%   on user-settings. In either case, new steiner points are
%   introduced to split "bad" triangles - those that violate
%   the set of prescribed constraints. In the "-DR" type pr-
%   ocess triangles are split about their circumballs. In
%   the "-FD" approach, new vertices are positioned such th-
%   at mesh-spacing and element-shape constraints are satis-
%   fied in a "locally-optimal" fashion.

    tcpu.full = +0. ;
    tcpu.dtri = +0. ;
    tcpu.tcon = +0. ;
    tcpu.ball = +0. ;
    tcpu.hfun = +0. ;
    tcpu.offc = +0. ;
    tcpu.filt = +0. ;

    vidx = (1:size(vert,1))';     %- "new" vert list to test

    tnow =  tic ;

    near = +.775;

    while (strcmpi(opts.ref2,'refine'))

        iter = iter + 1 ;

    %------------------------------------- build current CDT
        ttic = tic ;

        nold = size(vert,1) ;

       [vert,conn, ...
        tria,tnum]= deltri2(vert,conn, ...
                            node,PSLG, ...
                            part, ....
                            opts.dtri) ;

        nnew = size(vert,1) ;

        vidx = ...
       [vidx; (nold:nnew)'] ;

        % wjp: in case the new mesh has fewer nodes than old one
        vidx(vidx > nnew) = [];

        tcpu.dtri = ...
            tcpu.dtri + toc(ttic) ;

    %------------------------------------- build current adj
        ttic = tic ;

       [edge,tria]= tricon2(tria,conn) ;

        tcpu.tcon = ...
            tcpu.tcon + toc(ttic) ;

        if (iter>=opts.iter),break; end

    %------------------------------------- calc. circumballs
        ttic = tic ;

        bal1 = cdtbal1(vert,conn) ;
        bal2 = cdtbal2(vert, ...
                       edge,tria) ;
        len2 = minlen2(vert,tria) ;

        rho2 = bal2(:,+3) ./ len2 ;

    %------------------------------------- refinement scores
        scr2 = rho2 .* bal2(:,+3) ;

        tcpu.ball = ...
            tcpu.ball + toc(ttic) ;

    %------------------------------------- eval. length-fun.
        ttic = tic ;

        if (~isempty(hfun))
            if (isnumeric(hfun))
            fun0 = hfun * ...
              ones(size(vert,1),1);
            fun2 = hfun ;
            else
            fun0(vidx) = ...
                feval(hfun, ...
            vert(vidx,:), harg{:});
            fun0 = fun0(:) ;
            fun2 = fun0(tria(:,1))...
                 + fun0(tria(:,2))...
                 + fun0(tria(:,3));
            fun2 = fun2 / +3. ;
            end
        else
            fun0 = +inf * ...
              ones(size(vert,1),1);
            fun2 = +inf ;
        end

        siz2 = ...
         +3. * bal2(:,3)./(fun2.*fun2) ;

        tcpu.hfun = ...
            tcpu.hfun + toc(ttic) ;

    %------------------------------------- refinement queues
        ref1 = false(size(conn,1),1);
        ref2 = false(size(tria,1),1);

        stri = isfeat2(vert,edge,tria) ;

        ref2(rho2>opts.rho2* ...        %- bad rad-edge len.
                  opts.rho2) = true ;
        ref2(stri) = false ;
        ref2(siz2>opts.siz2* ...        %- bad equiv. length
                  opts.siz2) = true ;

        num2 = find(ref2);

    %------------------------------------- dump-out progess!
        if (mod(iter,opts.disp)==0)
            numc = size(conn,1) ;
            numt = size(tria,1) ;
            fprintf(+1, ...
            '%11i %18i %18i\n', ...
            [iter,numc,numt]) ;
        end

    %------------------------------------- nothing to refine
        if (isempty(num2)), break; end

       [scr2,idx2] = sort( ...
            scr2(num2),'descend');
        num2 = num2(idx2);

    %------------------------------------- refine "bad" tria
        switch (lower(opts.kind))
        case 'delaunay'
    %------------------------------------- do circ-ball pt's
        new2 = zeros(length(num2),3);
        new2(:,1:2) = bal2(num2,1:2);

        rmin = ...                      %- min. insert radii
            len2(num2)*(1.-eps^.75)^2 ;

        new2(:,  3) = max( ...
            bal2(num2,3)*near^2,rmin) ;

        case 'delfront'
    %-- off-centre scheme -- refine triangles by positioning
    %-- new vertices along a local segment of the voronoi
    %-- diagram, bounded by assoc. circmballs. New points
    %-- are placed to satisfy the worst of local mesh-length
    %-- and element-shape constraints.

        ttic = tic ;

    %------------------------------------- find frontal edge
       [lmin,emin] = ...
            minlen2(vert,tria(num2,:)) ;

        ftri = false(length(num2),1) ;
        epos = zeros(length(num2),1) ;
        tadj = zeros(length(num2),1) ;

        for ii = +1 : length(epos)
            epos(ii) = tria( ...
                num2(ii),emin(ii)+3) ;
        end

    %------------------------------------- find frontal tria
        for enum = +1 : +3

            eidx = tria(num2,enum+3) ;

            ftri = ...
            ftri | edge(eidx,5) > +0 ;

            ione = ...
                num2 ~= edge(eidx,3) ;
            itwo = ~ione ;

            tadj(ione) = ...
                edge(eidx(ione),3);
            tadj(itwo) = ...
                edge(eidx(itwo),4);

            okay = tadj > +0 ;
            tidx = tadj(okay);

            ftri(okay) = ...
            ftri(okay) | ~ref2(tidx) ;

        end

        if (~any(ftri))                 %- can this happen!?
        ftri = true(length(num2),+1) ;
        end

    %------------------------------------- locate offcentres
        emid = vert(edge(epos,+1),:) ...
             + vert(edge(epos,+2),:) ;
        emid = emid * +0.50 ;

        elen = sqrt(lmin(:));

    %------------------------------------- "voro"-type dist.
        vvec = bal2(num2,1:2)-emid ;
        vlen = sqrt(sum(vvec.^2,2));
        vvec = vvec ./ [vlen,vlen] ;

        hmid = fun0(edge(epos,+1),:) ...
             + fun0(edge(epos,+2),:) ;
        hmid = hmid * +0.50 ;

    %------------------------------------- "ball"-type dist.
        rtri = elen * opts.off2 ;
        rfac = elen * +0.50 ;
        dsqr = rtri.^2 - rfac.^2;
        doff = rtri + ...
            sqrt(max(+0.,dsqr)) ;

    %------------------------------------- "size"-type dist.
        dsiz = +sqrt(3.)/2. * hmid ;

    %------------------------------------- bind "safe" dist.
       [dist,ioff] = ...
          min([dsiz,doff,vlen],[],2) ;

    %------------------------------------- locate offcentres
        off2 = ...
        emid + [dist,dist] .* vvec ;

    %------------------------------------- iter. "size"-type
        for isub = +1 : +3
    %------------------------------------- eval. length-fun.
        if (~isempty(hfun))
            if (isnumeric(hfun))
            hprj = hfun * ...
              ones(size(off2,1),1) ;
            else
            hprj = feval( ...
                hfun,off2,harg{:}) ;
            hprj = hprj(:) ;
            end
        else
            hprj = +inf * ...
              ones(size(off2,1),1) ;
        end

    %------------------------------------- "size"-type dist.
        hprj = .33*hmid + .67*hprj ;

        dsiz = +sqrt(3.)/2. * hprj ;

        dsiz(dsiz<elen*.50) = +inf ;    %- edge-ball limiter
        dsiz(dsiz>vlen*.95) = +inf ;    %- circ-ball limiter

    %------------------------------------- bind "safe" dist.
       [dist,ioff] = ...
          min([dsiz,doff,vlen],[],2) ;

    %------------------------------------- locate offcentres
        off2 = ...
        emid + [dist,dist] .* vvec ;

        end

        orad = ...
        sqrt((elen*.5).^2 + dist.^2) ;

    %------------------------------------- do offcentre pt's
        new2 = ...
        zeros(length(find(ftri)),+3) ;
        new2(:,1:2) = off2(ftri,1:2) ;

        rmin = ...                      %- min. insert radii
            lmin(ftri)*(1.-eps^.75)^2 ;

        new2(:,  3) = max( ...
            (orad(ftri)*near).^2,rmin);

        tcpu.offc = ...
            tcpu.offc + toc (ttic) ;

        end % switch(lower(opts.kind))

    %------------------------------------- inter.-ball dist.
        ttic = tic ;

    %------------------------------------- proximity filters
       [vp,vi] = ...
          findball(new2,new2(:,1:2)) ;

        keep = true (size(new2,1),1) ;
        for ii = size(vp,1):-1:+1
            for ip = vp(ii,1) ...
                   : vp(ii,2)
                jj = vi(ip);
                if (keep(jj) && ...
                    keep(ii) && ...
                    jj < ii )

                keep(ii) = false ;
                break;

                end
            end
        end

        new2 = new2(keep,:);

    %------------------------------------- test encroachment
        bal1(:,3) = ...
            (1.-eps^.75) * bal1(:,3);

       [vp,vi] = ...
          findball(bal1,new2(:,1:2));

        keep = true (size(new2,1),1);
        for ii = +1:+1:size(vp,1)
            for ip = vp(ii,1) ...
                   : vp(ii,2)
                jj = vi(ip);
                ref1(jj) =  true ;
                keep(ii) = false ;
            end
        end

    %------------------------------------- leave sharp edges
        ebnd = false(size(edge,1),1);
        ebnd(tria(stri,4:6)) = true ;

        enot = ...
        setset2(conn,edge(ebnd,1:2));

        ref1(enot) = false ;

    %------------------------------------- preserve boundary
        if (strcmpi(opts.ref1,...
            'preserve'))
        ref1(:)    = false ;
        end

    %------------------------------------- refinement points
        new2 = new2(keep,:);
        new1 = bal1(ref1,:);

        tcpu.filt = ...
            tcpu.filt + toc(ttic) ;

    %------------------------------------- split constraints
        idx1 = ...
       (1:size(new1))'+size(vert,1) ;

        idx2 = ...
       (1:size(new2))'+size(new1,1) ...
                      +size(vert,1) ;

        cnew = [conn( ref1,1), idx1
                conn( ref1,2), idx1];
        conn = [conn(~ref1,:); cnew];

        vidx = [idx1; idx2];

    %------------------------------------- update vertex set
        nold = size(vert,1);
        vert = [vert; new1(:,1:2)];
        vert = [vert; new2(:,1:2)];
        % wjp: make sure vertices are unique
        vert = unique(vert,'rows','stable');
        nnew = size(vert,1);

        if (nnew == nold), break; end   %- we *must* be done

    end

    tcpu.full = ...
        tcpu.full + toc(tnow) ;

    if (~isinf(opts.disp) )
    %------------------------------------- print final stats
        numc = size(conn,1) ;
        numt = size(tria,1) ;
        fprintf(+1, ...
        '%11i %18i %18i\n', ...
        [iter,numc,numt]) ;
    end

    if (opts.dbug)
    %------------------------------------- print debug timer
        fprintf(1,'\n') ;
        fprintf(1,' 2-simplex REF. timer...\n');
        fprintf(1,'\n') ;
        fprintf(1, ...
        ' FULL: %f \n', tcpu.full);
        fprintf(1, ...
        ' DTRI: %f \n', tcpu.dtri);
        fprintf(1, ...
        ' TCON: %f \n', tcpu.tcon);
        fprintf(1, ...
        ' BALL: %f \n', tcpu.ball);
        fprintf(1, ...
        ' HFUN: %f \n', tcpu.hfun);
        fprintf(1, ...
        ' OFFC: %f \n', tcpu.offc);
        fprintf(1, ...
        ' FILT: %f \n', tcpu.filt);
        fprintf(1,'\n') ;
    end

end

function [opts] = makeopt(opts)
%MAKEOPT setup the options structure for REFINE2.

    if (~isfield(opts,'dtri'))
        opts.dtri = 'constrained';
    else
    if (~strcmpi(opts.dtri, 'conforming') && ...
        ~strcmpi(opts.dtri,'constrained') )
        error( ...
    'refine2:invalidOption','Invalid constraint DTRI.');
    end
    end

    if (~isfield(opts,'kind'))
        opts.kind = 'delfront';
    else
    if (~strcmpi(opts.kind, 'delfront') && ...
        ~strcmpi(opts.kind, 'delaunay') )
        error( ...
    'refine2:invalidOption','Invalid refinement KIND.');
    end
    end

    if (~isfield(opts,'ref1'))
        opts.ref1 = 'refine';
    else
    if (~strcmpi(opts.ref1,   'refine') && ...
        ~strcmpi(opts.ref1, 'preserve') )
        error( ...
    'refine2:invalidOption','Invalid refinement REF1.');
    end
    end

    if (~isfield(opts,'ref2'))
        opts.ref2 = 'refine';
    else
    if (~strcmpi(opts.ref2,   'refine') && ...
        ~strcmpi(opts.ref2, 'preserve') )
        error( ...
    'refine2:invalidOption','Invalid refinement REF2.');
    end
    end

    if (~isfield(opts,'iter'))
        opts.iter = +inf;
    else
    if (~isnumeric(opts.iter))
        error('refine2:incorrectInputClass', ...
            'Incorrect input class.');
    end
    if (numel(opts.iter)~= +1)
        error('refine2:incorrectDimensions', ...
            'Incorrect input dimensions.') ;
    end
    if (opts.iter <= +0)
        error('refine2:invalidOptionValues', ...
            'Invalid OPT.ITER selection.') ;
    end
    end

    if (~isfield(opts,'disp'))
        opts.disp = +10 ;
    else
    if (~isnumeric(opts.disp))
        error('refine2:incorrectInputClass', ...
            'Incorrect input class.');
    end
    if (numel(opts.disp)~= +1)
        error('refine2:incorrectDimensions', ...
            'Incorrect input dimensions.') ;
    end
    if (opts.disp <= +0)
        error('refine2:invalidOptionValues', ...
            'Invalid OPT.DISP selection.') ;
    end
    end

    if (~isfield(opts,'rho2'))
        opts.rho2 = 1.025;
    else
    if (~isnumeric(opts.rho2))
        error('refine2:incorrectInputClass', ...
            'Incorrect input class.');
    end
    if (numel(opts.rho2)~= +1)
        error('refine2:incorrectDimensions', ...
            'Incorrect input dimensions.') ;
    end
    if (opts.rho2 < +1.)
        error('refine2:invalidOptionValues', ...
            'Invalid OPT.RHO2 selection.') ;
    end
    end

    if (~isfield(opts,'off2'))
        opts.off2 = 0.933;
    else
    if (~isnumeric(opts.off2))
        error('refine2:incorrectInputClass', ...
            'Incorrect input class.');
    end
    if (numel(opts.off2)~= +1)
        error('refine2:incorrectDimensions', ...
            'Incorrect input dimensions.') ;
    end
    if (opts.off2 < +.7)
        error('refine2:invalidOptionValues', ...
            'Invalid OPT.OFF2 selection.') ;
    end
    end

    if (~isfield(opts,'siz1'))
        opts.siz1 = 1.333;
    else
    if (~isnumeric(opts.siz1))
        error('refine2:incorrectInputClass', ...
            'Incorrect input class.');
    end
    if (numel(opts.siz1)~= +1)
        error('refine2:incorrectDimensions', ...
            'Incorrect input dimensions.') ;
    end
    if (opts.siz1 <= 0.)
        error('refine2:invalidOptionValues', ...
            'Invalid OPT.SIZ1 selection.') ;
    end
    end

    if (~isfield(opts,'siz2'))
        opts.siz2 = 1.300;
    else
    if (~isnumeric(opts.siz2))
        error('refine2:incorrectInputClass', ...
            'Incorrect input class.');
    end
    if (numel(opts.siz2)~= +1)
        error('refine2:incorrectDimensions', ...
            'Incorrect input dimensions.') ;
    end
    if (opts.siz2 <= 0.)
        error('refine2:invalidOptionValues', ...
            'Invalid OPT.SIZ2 selection.') ;
    end
    end

    if (~isfield(opts,'dbug'))
        opts.dbug = false;
    else
    if (~islogical(opts.dbug))
        error('refine2:incorrectInputClass', ...
            'Incorrect input class.');
    end
    if (numel(opts.dbug)~= +1)
        error('refine2:incorrectDimensions', ...
            'Incorrect input dimensions.') ;
    end
    end

end


function h = supportFunction(polygon, varargin)
%SUPPORTFUNCTION Compute support function of a polygon.
%
%   H = supportFunction(POLYGON, N)
%   uses N points for suport function approximation
%
%   H = supportFunction(POLYGON)
%   assume 24 points for approximation
%
%   H = supportFunction(POLYGON, V)
%   where V is a vector, uses vector V of angles to compute support
%   function.
%
%   See also
%   polygons2d, convexification

% ------
% Author: David Legland
% E-mail: david.legland@inrae.fr
% Created: 2004-12-20
% Copyright 2004-2022 INRA - TPV URPOI - BIA IMASTE

N = 24;
u = 0:2*pi/N:2*pi*(1-1/N);

if length(varargin)==1
    var = varargin{1};
    if length(var)==1
        N = var;
        u = 0:2*pi/N:2*pi*(1-1/N);
    else
        u = var;
    end
end

% ensure u vertical vector
if size(u, 1)==1
    u=u';
end

h = zeros(size(u));

for i=1:length(u)

    v = repmat([cos(u(i)) sin(u(i))], [size(polygon, 1), 1]);

    h(i) = max(dot(polygon, v, 2));
end


classdef MOSSETracker < handle
    %MOSSETRACKER  MOSSE filter based tracker
    %
    % Correlation filter based tracking using MOSSE filters
    % (Minimum Output Sum of Squared Error), described in [Bolme2010].
    %
    % The target object appearance is modeled using adaptive correlation
    % filters, and tracking is performed via convolution.
    %
    % The filters are learnt online in an adaptive manner for visual tracking.
    %
    % ## Sources:
    %
    % * https://github.com/opencv/opencv/blob/3.3.1/samples/python/mosse.py
    %
    % ## References
    % [Bolme2010]:
    % > David S. Bolme et al.
    % > "Visual Object Tracking using Adaptive Correlation Filters"
    % > [PDF](http://www.cs.colostate.edu/~draper/papers/bolme_cvpr10.pdf)
    %

    properties
        % criteria for successful tracking, psr > min_psr
        % A value between [20,60] indicates very strong peaks,
        % less than 7 indicates bad track quality.
        min_psr = 8.0;
    end

    properties (Access = private)
        % Hanning window used when preprocessing images
        win
        % Fourier transform of 2D Gaussian shaped peak centered on target
        G
        % correlation filters/kernels
        H1
        H2
        H
        % last image of target
        last_img
        % last correlation response
        last_resp
    end

    properties (SetAccess = private)
        % center [x,y] of tracked object rectangle
        pos
        % size [w,h] of tracked object rectangle
        siz
        % Peak-to-Sidelobe Ratio (PSR)
        psr
    end

    properties (Dependent, SetAccess = private)
        % tracked object position, as a `[x y w h]` rectangle
        bbox
    end

    methods
        function obj = MOSSETracker(frame, rect)
            %MOSSETRACKER  Constructor
            %
            %     obj = MOSSETracker(frame, rect)
            %
            % ## Input
            % * __frame__ first frame with target to track
            % * __rect__ rectangle around target object `[x,y,w,h]`
            %

            % center and size of object rectangle (after DFT optimal padding)
            obj.siz = arrayfun(@(s) cv.getOptimalDFTSize(s), rect(3:4));
            obj.pos = floor((2*rect(1:2) + rect(3:4) - obj.siz) / 2);
            obj.pos = obj.pos + 0.5 * (obj.siz - 1);

            % create Hanning window of same size as rect
            obj.win = cv.createHanningWindow(obj.siz, 'Type','single');

            % create Gaussian shaped peak, centered and of same size as rect
            % (Kronecker delta with 1 at target center, 0 elsewhere)
            g = zeros(obj.siz(2), obj.siz(1), 'single');
            g(floor(end/2),floor(end/2)) = 1;
            g = cv.GaussianBlur(g, 'KSize',[-1 -1], 'SigmaX',2.0);
            g = g / max(g(:));

            % initialize correlation filters
            obj.G = cv.dft(g, 'ComplexOutput',true);
            obj.H1 = zeros(size(obj.G), class(obj.G));
            obj.H2 = zeros(size(obj.G), class(obj.G));

            % train filters on a bunch of augmented images
            % by applying small random affine perturbations
            img = cv.getRectSubPix(frame, obj.siz, obj.pos);
            for i=1:128
                a = obj.preprocess(random_warp(img));
                obj.update_kernels(a);
            end

            % process first frame
            obj.update(frame);
        end

        function update(obj, frame, rate)
            %UPDATE  Track object in new frame and update filters
            %
            %     obj.update(frame)
            %     obj.update(frame, rate)
            %
            % ## Input
            % * __frame__ new frame in which to track object
            % * __rate__ learning rate in [0,1] range, default 0.125
            %

            % crop and process image from current position
            obj.last_img = cv.getRectSubPix(frame, obj.siz, obj.pos);
            img = obj.preprocess(obj.last_img);

            % track target by correlating filter over image
            [obj.last_resp, d, obj.psr] = obj.correlate(img);

            % check that response peack is strong enough (using PSR)
            if obj.psr <= obj.min_psr
                return;
            end

            % update object position
            obj.pos = obj.pos + d;

            % crop and process image from new position
            obj.last_img = cv.getRectSubPix(frame, obj.siz, obj.pos);
            img = obj.preprocess(obj.last_img);

            % online update (filter training)
            if nargin < 3, rate = 0.125; end
            obj.update_kernels(img, rate);
        end

        function [img, f, resp] = visualize_state(obj)
            %VISUALIZE_STATE  Visualize tracker state
            %
            %     [img, f, resp] = obj.visualize_state()
            %
            % ## Output
            % * __img__ cropped object image
            % * __f__ correlation filter, 0-freq centered and 8-bit normalized
            % * __resp__ correlation response, 8-bit normalized and clipped
            %

            % image
            img = obj.last_img;

            % kernel
            f = cv.dft(obj.H, 'Inverse',true, 'Scale',true, 'RealOutput',true);
            f = circshift(f, floor(-[size(f,1) size(f,2)]/2));
            if true
                f = (f - min(f(:))) / (max(f(:)) - min(f(:)));
            else
                f = cv.normalize(f, 'NormType','MinMax');
            end
            f = uint8(255 * f);

            % response
            if true
                resp = obj.last_resp / max(obj.last_resp(:));
            else
                resp = cv.normalize(obj.last_resp, 'NormType','Inf');
            end
            resp = uint8(255 * min(max(resp, 0), 1));
        end

        function vis = draw_object(obj, vis)
            %DRAW_OBJECT  Draw current location of tracked object
            %
            %     vis = obj.draw_object(vis)
            %
            % ## Input
            % * __vis__ input image
            %
            % ## Output
            % * __vis__ output image with drawn object
            %

            clr = {'Color',[0 0 255]};
            p1 = fix(obj.pos - 0.5 * obj.siz);
            p2 = fix(obj.pos + 0.5 * obj.siz);

            % draw location
            vis = cv.rectangle(vis, p1, p2, 'Thickness',2, clr{:});
            if obj.psr > obj.min_psr
                % good track, draw center
                vis = cv.circle(vis, fix(obj.pos), 2, 'Thickness',-1, clr{:});
            else
                % bad track, draw cross
                vis = cv.line(vis, [p1; p2(1) p1(2)], [p2; p1(1) p2(2)], clr{:});
            end

            % draw PSR value
            vis = cv.putText(vis, sprintf('PSR: %.2f', obj.psr), ...
                [p1(1) p2(2)+16], 'FontFace','HersheyPlain', ...
                'Thickness',2, 'LineType','AA', clr{:});
        end

        function bbox = get.bbox(obj)
            p1 = obj.pos - 0.5 * obj.siz;
            p2 = obj.pos + 0.5 * obj.siz;
            bbox = cv.Rect.from2points(p1, p2);
        end
    end

    methods (Access = private)
        function img = preprocess(obj, img)
            %PREPROCESS  Process input frame
            %
            %     img = obj.preprocess(img)
            %
            % ## Input
            % * __img__ input image
            %
            % ## Output
            % * __img__ output processed image
            %

            % log transform, normalize, and multiply by Hanning window
            img = log(single(img) + 1);
            if true
                img = img / norm(img(:));
            elseif true
                img = cv.normalize(img, 'NormType','L2');
            else
                img = (img - mean(img(:))) / std(img(:));
            end
            img = img .* obj.win;
        end

        function [resp, d, psr] = correlate(obj, img)
            %CORRELATE  Correlate filter over image and find peak in response
            %
            %     [resp, d, psr] = obj.correlate(img)
            %
            % ## Input
            % * __img__ processed image
            %
            % ## Output
            % * __resp__ correlation response
            % * __d__ offset of peak location from center `[x,y]`
            % * __psr__ PSR value, a measure of correlation peak strength. Can
            %   be used to stop the online update if PSR is too low, which is
            %   an indication the the object is occluded or tracking has
            %   failed.
            %

            % correlation (performed in frequency domain)
            resp = cv.mulSpectrums(cv.dft(img, 'ComplexOutput',true), ...
                obj.H, 'ConjB',true);
            resp = cv.dft(resp, 'Inverse',true, 'Scale',true, 'RealOutput',true);

            % max value location in correlation response
            [mval, midx] = max(resp(:));
            [my,mx] = ind2sub(size(resp), midx);
            mloc = [mx my]; %TODO: -1 ??

            % offset from center
            d = mloc - floor([size(resp,2) size(resp,1)] / 2);

            % compute PSR
            sidelobe = cv.rectangle(resp, mloc-5, mloc+5, ...
                'Color',NaN, 'Thickness',-1);
            sidelobe = sidelobe(~isnan(sidelobe));
            psr = (mval - mean(sidelobe)) / std(sidelobe);
        end

        function update_kernels(obj, img, rate)
            %UPDATE_KERNELS  Update correlation kernels
            %
            %     obj.update_kernels(img)
            %     obj.update_kernels(img, rate)
            %
            % ## Input
            % * __img__ processed image
            % * __rate__ learning rate
            %

            F = cv.dft(img, 'ComplexOutput',true);
            H_1 = cv.mulSpectrums(obj.G, F, 'ConjB',true);
            H_2 = cv.mulSpectrums(    F, F, 'ConjB',true);

            if nargin < 3
                % initialization phase
                obj.H1 = obj.H1 + H_1;
                obj.H2 = obj.H2 + H_2;
            else
                % update phase
                obj.H1 = obj.H1 * (1 - rate) + H_1 * rate;
                obj.H2 = obj.H2 * (1 - rate) + H_2 * rate;
            end

            obj.H = complex(obj.H1(:,:,1), obj.H1(:,:,2)) ./ ...
                    complex(obj.H2(:,:,1), obj.H2(:,:,2));
            obj.H = cat(3, real(obj.H), -imag(obj.H));
        end
    end
end

function img = random_warp(img, coef)
    %RANDOM_WARP  Warp image using a random affine transformation
    %
    %     img = random_warp(img)
    %     img = random_warp(img, coef)
    %
    % ## Input
    % * __img__ input image
    %
    % ## Output
    % * __img__ output warped image
    % * __coef__ randomness coefficient, default 0.2
    %

    if nargin < 2, coef = 0.2; end
    ang = (rand() - 0.5) * coef;
    T = [cos(ang) -sin(ang); sin(ang) cos(ang)];
    T = T + (rand(2) - 0.5) * coef;

    sz = [size(img,2); size(img,1)];
    c = fix(sz / 2);
    T(:,3) = c - T * c;

    img = cv.warpAffine(img, T, 'DSize',sz, 'BorderType','Reflect');
end


function [ishappy, cutoff] = standardCheck(f, values, data, pref)
%STANDARDCHECK   Attempt to trim trailing Fourier coefficients in a TRIGTECH.
%   [ISHAPPY, CUTOFF] = STANDARDCHECK(F) uses the routine STANDARDCHOP to
%   compute a positive integer CUTOFF which represents the number of
%   coefficients of F that are deemed accurate enough to keep.  ISHAPPY is TRUE
%   if the CUTOFF value returned by STANDARDCHOP is less than LENGTH(F) and
%   FALSE otherwise.
%
%   [ISHAPPY, CUTOFF] = STANDARDCHECK(F, VALUES, DATA, PREF) allows additional
%   preferences to be passed. VALUES is a matrix of the function values of F at
%   the corresponding interpolation points. DATA.VSCALE is an approximation of
%   the maximum function value of F on a possibly larger approximation
%   interval.  PREF is a data structure used to pass in additional information,
%   e.g. a target accuracy tolerance could be passed using PREF.CHEBFUNEPS.
%
% See also CLASSICCHECK, STRICTCHECK, LOOSECHECK.

% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

% Grab the coefficients of F.
coeffs = abs(f.coeffs(end:-1:1,:));
[n, m] = size(coeffs);

% Compute some values if none were given.
if ( nargin < 2 || isempty(values) )
    values = f.coeffs2vals(f.coeffs);
end

% In order to work with STANDARDCHOP, the coefficients of F are modified so that
% the entries corresponding to wave numbers k and -k appear sequentially in the
% new matrix of coefficients. These entries are also replaced by the sum of the
% absolute values of the k and -k coefficients.

% Need to handle odd/even cases separately.
isEven = mod(n, 2) == 0;
if ( isEven )
    coeffs = [coeffs(n,:) ; coeffs(n-1:-1:n/2+1,:) + coeffs(1:n/2-1,:) ; coeffs(n/2,:)];
else
    coeffs = [coeffs(n:-1:(n+1)/2+1,:) + coeffs(1:(n+1)/2-1,:) ; coeffs((n+1)/2,:)];
end
coeffs = flipud(coeffs);
coeffs = [coeffs(1,:) ; kron(coeffs(2:end,:), [1 ; 1])];

% Initialize ISHAPPY.
ishappy = false;

% Initialize CUTOFF.
cutoff = n;

% NaNs are not allowed.
if ( any(isnan(coeffs)) )
    error('CHEBFUN:TRIGTECH:standardCheck:nanEval', ...
        'Function returned NaN when evaluated.')
end

% Grab some preferences.
if ( nargin == 1 )
    pref = f.techPref();
    tol = pref.chebfuneps;
elseif ( isnumeric(pref) )
    tol = pref;
else
    tol = pref.chebfuneps;
end

% Reshape TOL.
if ( size(tol, 2) ~= m )
  tol = ones(1, m)*max(tol);
end

% Scale TOL by VSCL/||F||;
nrmf = max(abs(values), [], 1);
tol = tol.*data.vscale./nrmf;

% Loop through columns of coeffs
ishappy = false(1, m);
cutoff = zeros(1, m);
for k = 1:m

    % Call STANDARDCHOP.
    cutoff(k) = standardChop(coeffs(:,k), tol(k));

    % Check for happiness.
    ishappy(k) = ( cutoff(k) < n );

    % Divide CUTOFF by 2.
    if ( mod(cutoff(k), 2) == 0 )
        cutoff(k) = cutoff(k)/2;
    else
        cutoff(k) = (cutoff(k) - 1)/2;
    end

    % Break if unhappy.
    if ( ~ishappy(k) )
        break
    end

end

% Set outputs.
ishappy = all(ishappy);

% CUTOFF is always odd.
cutoff = 2*max(cutoff) + 1;

end


function [Q_mean]=imlabelMean(M,ML)

% function [Q_mean]=imlabelMean(M,ML)
% ------------------------------------------------------------------------
%
% This function takes the mean for each of the labeled groups (NaN's
% ignored) in ML according to the intensities in M.
%
%
% Kevin Mattheus Moerman
% kevinmoerman@hotmail.com
% 2014/04/01
%------------------------------------------------------------------------

defaultMethod=1; %Hard coded for now
switch defaultMethod
    case 1 %SPARSE ARRAY BASED (more efficient for large arrays)
        labelSet=unique(ML(:));
        labelSet=labelSet(~isnan(labelSet)); %Remove the nan labelled group

        numLabels=max(labelSet);

        logic_ML_not_nan=~isnan(ML);
        nnzQ=nnz(logic_ML_not_nan);
        Iq=ML(logic_ML_not_nan);
        Jq=1:nnzQ;
        Sq=M(logic_ML_not_nan);
        sizQ=[numLabels,nnzQ];
        nnzQ=nnz(logic_ML_not_nan);
        Q=sparse(Iq,Jq,Sq,sizQ(1),sizQ(2),nnzQ);

        Q_sum=full(sum(Q,2));
        Q_voxelCount=full(sum(spones(Q),2));
        Q_mean=Q_sum./Q_voxelCount;
    case 2 %REGIONPROPS BASED
        ML(isnan(ML))=0;
        A=regionprops(ML,M,'MeanIntensity');
        Q_mean=[A.MeanIntensity];
        Q_mean=Q_mean(:);
end

%%
% _*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 [varargout]=quad4_quad8(varargin)

% function [QUAD8,V8,VX8C]=quad4_quad8(QUAD4,V4,VXC)
%
% This function converts 4 node (e.g. linear) quadrilaterial elements into
% 8 node (e.g. quadratic) quadrilateral elements compatible with FEBio.
%
% Kevin Mattheus Moerman
% gibbon.toolbox@gmail.com
%
% 2018/10/19 Created based on tri3_tri6
%----------------------------------------------------------------------
%%

switch nargin
    case 2
        QUAD4=varargin{1};
        V4=varargin{2};
        VXC={};
    case 3
        QUAD4=varargin{1};
        V4=varargin{2};
        VXC=varargin{3};
end

%%

E=[QUAD4(:,[1 2]); QUAD4(:,[2 3]); QUAD4(:,[3 4]); QUAD4(:,[4 1])]; %Edges matrix
Es=sort(E,2); %Sorted edges matrix
[~,ind1,~]=unique(Es,'rows'); %Indices for unique edges
E=E(ind1,:); %The unique esges

numPoints = size(V4,1);
numEdges = size(E,1);

% Get indices of the three edges associated with each face
A = sparse(E(:,1),E(:,2),(1:numEdges)+numPoints,numPoints,numPoints,numEdges);
A = max(A,A'); %Copy symmetric

%Indices for A matrix
indA_12=QUAD4(:,1)+(QUAD4(:,2)-1)*numPoints;
indA_23=QUAD4(:,2)+(QUAD4(:,3)-1)*numPoints;
indA_34=QUAD4(:,3)+(QUAD4(:,4)-1)*numPoints;
indA_41=QUAD4(:,4)+(QUAD4(:,1)-1)*numPoints;

%Get indices for vertex array
indV_12=full(A(indA_12));
indV_23=full(A(indA_23));
indV_34=full(A(indA_34));
indV_41=full(A(indA_41));

%Create faces array
QUAD8=[QUAD4(:,1) QUAD4(:,2) QUAD4(:,3) QUAD4(:,4) indV_12 indV_23 indV_34 indV_41];

%Create vertex array
Vn=0.5*(V4(E(:,1),:)+V4(E(:,2),:)); %new mid-edge points
V8 = [V4; Vn]; %Join point sets

%%
varargout{1}=QUAD8;
varargout{2}=V8;

if nargout==3
    %Derive VX8C
    if ~isempty(VXC)
        VX8C=VXC;
        for q=1:1:numel(VXC)
            VX=VXC{q};
            VX_1_4=VX;
            VX_5_8=0.5*(VX(E(:,1),:)+VX(E(:,2),:)); %new mid-edge data
            VX8=[VX_1_4; VX_5_8];
            VX8C{q}=VX8;
        end
    else
        VX8C={};
    end
    varargout{3}=VX8C;
end

%%
% _*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 [accuracy,predictlabel] = SRKDApredict(fea, gnd, model)
% SRKDApredict: Spectral Regression Kernel Discriminant Analysis Prediction
%               SRKDApredict use SRKDA as a classifier. It used the nearest
%               center rule in the SRKDA subspace for classification.
%
%       [predictlabel,accuracy,elapse] = SRKDApredict(fea, gnd, model);
%
%             Input:
%
%               fea     - data matrix. Each row is a data point.
%               gnd     - Label vector of fea.
%             model     - model trained by SRKDAtrain.m
%
%             Output:
%
%            accuracy   - classification accuracy
%         predictlabel  - predict label for fea
%
%    Examples:
%
%
% See also SRKDAtrain, KSR, KSR_caller
%
%Reference:
%
%   [1] Deng Cai, Xiaofei He, and Jiawei Han. "Speed Up Kernel Discriminant
%   Analysis", The VLDB Journal, vol. 20, no. 1, pp. 21-33, January, 2011.
%
%   [2] Deng Cai, Xiaofei He and Jiawei Han, "SRDA: An Efficient Algorithm for
%   Large Scale Discriminant Analysis" IEEE Transactions on Knowledge and
%   Data Engineering, vol. 20, no. 1, pp. 1-12, January, 2008.
%
%   [3] Deng Cai, "Spectral Regression: A Regression Framework for
%   Efficient Regularized Subspace Learning", PhD Thesis, Department of
%   Computer Science, UIUC, 2009.
%
%   version 3.0 --Jan/2012
%   version 2.0 --December/2011
%   version 1.0 --May/2006
%
%   Written by Deng Cai (dengcai AT gmail.com)
%

MAX_MATRIX_SIZE = 8000; % You can change this number based on your memory.

if ~strcmp(model.TYPE,'SRKDA')
    error('model does not match!');
end

nTrain = size(model.fea,1);
nTest = size(fea,1);
nBlock = ceil(MAX_MATRIX_SIZE*MAX_MATRIX_SIZE/nTrain);
if model.LARs
    accuracy = zeros(length(model.LassoCardi),1);
    predictlabel = zeros(nTest,length(model.LassoCardi));
    Embed_Test = cell(length(model.LassoCardi),1);
    for i=1:length(model.LassoCardi)
        Embed_Test{i} = zeros(nTest,size(model.projection{i},2));
    end
    for j = 1:ceil(nTest/nBlock)
        if j == ceil(nTest/nBlock)
            smpIdx = (j-1)*nBlock+1:nTest;
        else
            smpIdx = (j-1)*nBlock+1:j*nBlock;
        end
        KTest= constructKernel(fea(smpIdx,:),model.fea,model.options);
        if model.bSemi
            KTest = KTest*model.KtestHat;
        end
        for i=1:length(model.LassoCardi)
            if model.bSemi
                Embed_Test{i}(smpIdx,:) = KTest(:,1:model.nLabel)*model.projection{i};
            else
                Embed_Test{i}(smpIdx,:) = KTest*model.projection{i};
            end
        end
        clear KTest;
    end

    for i=1:length(model.LassoCardi)
        D = EuDist2(Embed_Test{i},model.ClassCenter{i},0);
        [dump, idx] = min(D,[],2);
        predictlabel(:,i) = model.ClassLabel(idx);
        accuracy(i) = 1 - length(find(predictlabel(:,i)-gnd))/nTest;
    end
else
    Embed_Test = zeros(nTest,size(model.projection,2));
    for i = 1:ceil(nTest/nBlock)
        if i == ceil(nTest/nBlock)
            smpIdx = (i-1)*nBlock+1:nTest;
        else
            smpIdx = (i-1)*nBlock+1:i*nBlock;
        end
        KTest= constructKernel(fea(smpIdx,:),model.fea,model.options);
        if model.bSemi
            KTest = KTest*model.KtestHat;
            Embed_Test(smpIdx,:) = KTest(:,1:model.nLabel)*model.projection;
        else
            Embed_Test(smpIdx,:) = KTest*model.projection;
        end
        clear KTest;
    end
    D = EuDist2(Embed_Test,model.ClassCenter,0);
    [dump, idx] = min(D,[],2);
    predictlabel = model.ClassLabel(idx);
    accuracy = 1 - length(find(predictlabel-gnd))/nTest;
end


 function xs = eml_osps(x, Gb, yi, ci, ri, niter, pixmax, curv, relax0, chat)
%function xs = eml_osps(x, Gb, yi, ci, ri, niter, pixmax, curv, relax0, chat)
% E-ML-OSPS algorithm for emission Poisson problem
% (ordered subsets separable paraboloidal surrogates)
% model: Y_i ~ Poisson(c_i [G x]_i + r_i)
% in
%	x	[np,1]		initial estimate
%	Gb			Gblock object (see eml_osps_test.m)
%	yi,ci,ri [nb,na]	see em_fbp.m (for model too)
%	niter			# iterations
%	pixmax			upper constraint for pixel values
%	curv	'oc' for erdogan's optimal curvatures
%		'pc' for erdogan's fast precomputed curvatures,
%			which usually provides faster convergence,
%			but can be nonmonotone
%	relax0	[1] or [2]	relax0 or (relax0, relax_rate)
%	chat
% out
%	xs [np,niter]	updated image vectors each iteration
%
% Copyright Mar 2000, Jeff Fessler, The University of Michigan

if nargin < 3, ir_usage, end

nblock = block_ob(Gb, 'n');
starts = subset_start(nblock);

if ~isvar('ci') || isempty(ci)
	ci = ones(size(yi));
end
if ~isvar('ri') || isempty(ri)
	ri = zeros(size(yi));
end
if ~isvar('niter') || isempty(niter),	niter = 1;	end
if ~isvar('pixmax') || isempty(pixmax),	pixmax = inf;	end
if ~isvar('curv') || isempty(curv),	curv = 'oc';	end
if ~isvar('chat') || isempty(chat),	chat = true;	end

if ~isvar('relax0') || isempty(relax0)
	relax0 = 1;
end
if length(relax0) == 1
	relax_rate = 0;
elseif length(relax0) == 2
	relax_rate = relax0(2);
	relax0 = relax0(1);
else
	error relax
end

eml_check(yi, ci, ri);

[nb, na] = size(yi);

gi = sum(Gb')';		% g_i = sum_j g_ij
gi = reshape(gi, nb, na);

%
%	precomputed curvatures
%
denom = zeros(numel(x), nblock);
if streq(curv, 'pc')
	ni = eml_curvature(yi, ci, ri, [], [], curv);
	denom = Gb' * col(gi .* ni);
%	printf('ni range %g %g', min(ni(:)), max(ni(:)))
%	printf('denom range %g %g', min(denom(:)), max(denom(:)))
end

%
%	loop over iterations
%
xs = zeros(numel(x), niter);
x = max(x,0);
x = min(x,pixmax);
xs(:,1) = x;
for iter = 2:niter

	relax = 1;

	%
	% loop over subsets
	%
	for iset=1:nblock
		iblock = starts(iset);
		ia = iblock:nblock:na;

		li = Gb{iblock} * x;			% l=G*x "line integrals"
		li = reshape(li, nb, length(ia));
		yb = ci(:,ia) .* li + ri(:,ia);		% predicted meas. means

		% fix: need to be careful here with 0/0 -> 0
		dothi = ci(:,ia) .* (yi(:,ia) ./ yb - 1);

		% non-precomputed curvatures (notably, optimal curvature),
		% for ensured monotone increase
		if ~streq(curv, 'pc')
			ni = eml_curvature(yi(:,ia), ci(:,ia), ri(:,ia), li, yb, curv);
			denom = nblock * (Gb{iblock}' * col(gi(:,ia) .* ni));
		end

		grad = Gb{iblock}' * dothi(:);
		num = nblock * grad;
		x = x + relax * num ./ denom;	% relaxed update
		x = max(x,0);			% lower bound
		x = min(x,pixmax);		% upper bound
	end

	xs(:,iter) = x;
end


function [U_final, V_final, nIter_final, objhistory_final] = LCCF_Multi(K, k, W, options, U, V)
% Locally Consistant Concept Factorization (LCCF)
%
% where
%   X
% Notation:
% K ... (nSmp x nSmp) kernel matrix
% k ... number of hidden factors
% W ... weight matrix of the affinity graph
%
% options ... Structure holding all settings
%
% You only need to provide the above four inputs.
%
% X = X*U*V'
%
% References:
% [1] Deng Cai, Xiaofei He, Jiawei Han, "Locally Consistent Concept
%     Factorization for Document Clustering", IEEE Transactions on Knowledge
%     and Data Engineering, Vol. 23, No. 6, pp. 902-913, 2011.
%
%
%   version 2.0 --April/2010
%   version 1.0 --Dec./2008
%
%   Written by Deng Cai (dengcai AT gmail.com)
%

differror = options.error;
maxIter = options.maxIter;
nRepeat = options.nRepeat;
minIterOrig = options.minIter;
minIter = minIterOrig-1;
meanFitRatio = options.meanFitRatio;

alpha = options.alpha;

Norm = 2;
NormV = 1;

nSmp = size(K,1);

if alpha < 0
    alpha = 0;
end
W = alpha*W;
DCol = full(sum(W,2));
D = spdiags(DCol,0,speye(size(W,1)));
L = D - W;
if isfield(options,'NormW') && options.NormW
    D_mhalf = DCol.^-.5;

    tmpD_mhalf = repmat(D_mhalf,1,nSmp);
    L = (tmpD_mhalf.*L).*tmpD_mhalf';
    clear D_mhalf tmpD_mhalf;

    L = max(L, L');
end

if isempty(U)
    U = abs(rand(nSmp,k));
    V = abs(rand(nSmp,k));
else
    nRepeat = 1;
end
[U,V] = NormalizeUV(K, U, V, NormV, Norm);

selectInit = 1;
if nRepeat == 1
    selectInit = 0;
    minIterOrig = 0;
    minIter = 0;
    if isempty(maxIter)
        [obj_NMFhistory, obj_Laphistory] = CalculateObj(K, U, V, L);
        objhistory = obj_NMFhistory + obj_Laphistory;
        meanFit = objhistory*10;
    else
        if isfield(options,'Converge') && options.Converge
            [obj_NMFhistory, obj_Laphistory] = CalculateObj(K, U, V, L);
            objhistory = obj_NMFhistory + obj_Laphistory;
        end
    end
else
    if isfield(options,'Converge') && options.Converge
        error('Not implemented!');
    end
end

tryNo = 0;
while tryNo < nRepeat
    tmp_T = cputime;
    tryNo = tryNo+1;
    nIter = 0;
    maxErr = 1;
    while(maxErr > differror)
        % ===================== update V ========================
        KU = K*U;               % n^2k
        UKU = U'*KU;            % nk^2
        VUKU = V*UKU;           % nk^2

        if alpha > 0
            WV = W*V;
            DV = repmat(DCol,1,k).*V;

            KU = KU + WV;
            VUKU = VUKU + DV;
        end

        V = V.*(KU./max(VUKU,1e-10));
        clear WV DV KU UKU VUKU;
        % ===================== update U ========================
        KV = K*V;               % n^2k
        VV = V'*V;              % nk^2
        KUVV = K*U*VV;          % n^2k

        U = U.*(KV./max(KUVV,1e-10));
        clear KV VV KUVV;

        nIter = nIter + 1;
        if nIter > minIter
            [U,V] = NormalizeUV(K, U, V, NormV, Norm);
            if selectInit
                [obj_NMFhistory, obj_Laphistory] = CalculateObj(K, U, V, L);
                objhistory = obj_NMFhistory + obj_Laphistory;
                maxErr = 0;
            else
                if isempty(maxIter)
                    [obj_NMF, obj_Lap] = CalculateObj(K, U, V, L);
                    newobj = obj_NMF + obj_Lap;
                    objhistory = [objhistory newobj]; %#ok<AGROW>
                    meanFit = meanFitRatio*meanFit + (1-meanFitRatio)*newobj;
                    maxErr = (meanFit-newobj)/meanFit;
                else
                    if isfield(options,'Converge') && options.Converge
                        [obj_NMF, obj_Lap] = CalculateObj(K, U, V, L);
                        newobj = obj_NMF + obj_Lap;
                        objhistory = [objhistory newobj]; %#ok<AGROW>
                    end
                    maxErr = 1;
                    if nIter >= maxIter
                        maxErr = 0;
                        if isfield(options,'Converge') && options.Converge
                        else
                            objhistory = 0;
                        end
                    end
                end
            end
        end
    end

    if tryNo == 1
        U_final = U;
        V_final = V;
        nIter_final = nIter;
        objhistory_final = objhistory;
    else
        if objhistory(end) < objhistory_final(end)
            U_final = U;
            V_final = V;
            nIter_final = nIter;
            objhistory_final = objhistory;
        end
    end

    if selectInit
        if tryNo < nRepeat
            %re-start
            U = abs(rand(nSmp,k));
            V = abs(rand(nSmp,k));

            [U,V] = NormalizeUV(K, U, V, NormV, Norm);
        else
            tryNo = tryNo - 1;
            minIter = 0;
            selectInit = 0;
            U = U_final;
            V = V_final;
            objhistory = objhistory_final;
            meanFit = objhistory*10;
        end
    end
end

nIter_final = nIter_final + minIterOrig;

Norm = 2;
NormV = 0;

[U_final,V_final] = NormalizeUV(K, U_final, V_final, NormV, Norm);

%==========================================================================

function [obj_NMF, obj_Lap, dV] = CalculateObj(K, U, V, L, deltaVU, dVordU)
if ~exist('deltaVU','var')
    deltaVU = 0;
end
if ~exist('dVordU','var')
    dVordU = 1;
end
dV = [];

UK = U'*K; % n^2k
UKU = UK*U; % nk^2
VUK = V*UK; % n^2k
VV = V'*V; % nk^2
obj_NMF = sum(diag(K))-2*sum(diag(VUK))+sum(sum(UKU.*VV));
if deltaVU
    if dVordU
        dV = V*UKU - UK'+ L*V; %nk^2
    else
        dV = (VUK-K)'*V;  %n^2k
    end
end
obj_Lap = sum(sum((L*V).*V));

function [U, V] = NormalizeUV(K, U, V, NormV, Norm)
k = size(U,2);
if Norm == 2
    if NormV
        norms = max(1e-15,sqrt(sum(V.^2,1)))';
        V = V*spdiags(norms.^-1,0,k,k);
        U = U*spdiags(norms,0,k,k);
    else
        norms = max(1e-15,sqrt(sum(U.*(K*U),1)))';
        U = U*spdiags(norms.^-1,0,k,k);
        V = V*spdiags(norms,0,k,k);
    end
else
    if NormV
        norms = max(1e-15,sum(abs(V),1))';
        V = V*spdiags(norms.^-1,0,k,k);
        U = U*spdiags(norms,0,k,k);
    else
        norms = max(1e-15,sum(U.*(K*U),1))';
        U = U*spdiags(norms.^-1,0,k,k);
        V = V*spdiags(norms,0,k,k);
    end
end


% VL_VLAD   VLAD feature encoding
%   ENC = VL_VLAD(X, MEANS, ASSIGNMENTS) computes the VLAD
%   encoding of the vectors X relative to cluster centers MEANS and
%   vector-to-cluster soft assignments ASSIGNMENTS.
%
%   X has one column per data vector (e.g. a SIFT descriptor), and
%   MEANS has one column per component. Usually one has one component
%   per KMeans cluster and MEANS are the KMeans centers. X and MEANS
%   have the same number of rows and the data class, which can be
%   either SINGLE or DOUBLE.
%
%   ASSIGNMENTS has as many rows as clusters and as many columns as
%   X. Its columns are non-negative and should sum to one,
%   representing the soft assignment of the corresponding vector in X
%   to each of the clusters. It is of the same class as X.
%
%   ENC is a vector of the same class of X of size equal to the
%   product of the data dimension and the number of clusters.
%
%   By default, ENC is L2 normalized. VL_VLAD() accepts the following
%   options:
%
%   Unnormalized::
%     If specified, no overall normalization is applied to ENC.
%
%   NormalizeComponents::
%     If specified, the part of the encoding corresponding to each
%     cluster is individually normalized.
%
%   NormalizeMass::
%     If specified, each component is re-normalized by the mass
%     of data vectors assigned to it. If NormalizedComponents is
%     also selected, this has no effect.
%
%   SquareRoot::
%     If specified, the signed square root function is applied to
%     ENC before normalization.
%
%   Verbose::
%     Increase the verbosity level (may be specified multiple times).
%
%   See: VLAD, VL_HELP().

% Authors: David Novotny and Andrea Vedaldi

% Copyright (C) 2013 David Novotny and Andrea Vedaldi
% All rights reserved.
%
% This file is part of the VLFeat library and is made available under
% the terms of the BSD license (see the COPYING file).


function M = hmxSpy(varargin)
%+========================================================================+
%|                                                                        |
%|         OPENHMX - LIBRARY FOR H-MATRIX COMPRESSION AND ALGEBRA         |
%|           openHmx is part of the GYPSILAB toolbox for Matlab           |
%|                                                                        |
%| COPYRIGHT : Matthieu Aussal (c) 2017-2018.                             |
%| PROPERTY  : Centre de Mathematiques Appliquees, Ecole polytechnique,   |
%| route de Saclay, 91128 Palaiseau, France. All rights reserved.         |
%| LICENCE   : This program is free software, distributed in the hope that|
%| it will be useful, but WITHOUT ANY WARRANTY. Natively, you can use,    |
%| redistribute and/or modify it under the terms of the GNU General Public|
%| License, as published by the Free Software Foundation (version 3 or    |
%| later,  http://www.gnu.org/licenses). For private use, dual licencing  |
%| is available, please contact us to activate a "pay for remove" option. |
%| CONTACT   : matthieu.aussal@polytechnique.edu                          |
%| WEBSITE   : www.cmap.polytechnique.fr/~aussal/gypsilab                 |
%|                                                                        |
%| Please acknowledge the gypsilab toolbox in programs or publications in |
%| which you use it.                                                      |
%|________________________________________________________________________|
%|   '&`   |                                                              |
%|    #    |   FILE       : hmxSpy.m                                      |
%|    #    |   VERSION    : 0.40                                          |
%|   _#_   |   AUTHOR(S)  : Matthieu Aussal                               |
%|  ( # )  |   CREATION   : 14.03.2017                                    |
%|  / 0 \  |   LAST MODIF : 14.03.2018                                    |
%| ( === ) |   SYNOPSIS   : Spy H-Matrix architecture                     |
%|  `---'  |                                                              |
%+========================================================================+

% Input data
Mh = varargin{1};

% Initialize visu block
if (Mh.typ > 0)
    M        = sparse(size(Mh,1),size(Mh,2));
    M(:,1)   = 1;
    M(:,end) = 1;
    M(1,:)   = 1;
    M(end,:) = 1;
end

%%%% H-Matrix (recursion)
if (Mh.typ == 0)
    A = hmxSpy(Mh.chd{1},[]);
    B = hmxSpy(Mh.chd{2},[]);
    C = hmxSpy(Mh.chd{3},[]);
    D = hmxSpy(Mh.chd{4},[]);
    M = [A,B;C,D];

%%% Compressed leaf
elseif (Mh.typ == 1)
    rk = size(Mh.dat{1},2);
    if (rk > 0)
        M(:,rk) = 1;
        M(rk,:) = 1;
    else
        M = 3 .* M;
    end

%%% Full leaf
elseif (Mh.typ == 2)
    if issparse(Mh.dat)
        M = 4 .* (M + (abs(Mh.dat)>0));
    else
        M = 2 .* M;
    end

%%% Unknown type
else
    error('hmxSpy.m : unavailable case')
end

% Graphical representation
if (length(varargin) == 1)
    spy(M==1,'b')
    hold on
    spy(M==2,'r')
    spy(M==3,'g')
    spy(M==4,'m')
    hold off
    title('openHmx : H-Matrix structure');
end
end


function a = log10(a)
%LOG10        Gradient logarithm  log10(a)
%

% written  12/06/98     S.M. Rump
% modified 10/14/00     S.M. Rump  use Tony's trick
% modified 04/04/04     S.M. Rump  set round to nearest for safety
%                                    accelaration for sparse input
% modified 04/06/05     S.M. Rump  rounding unchanged
% modified 09/28/08     S.M. Rump  check for rounding to nearest improved
% modified 10/08/08     S.M. Rump  improved sparse multiplication: not using intval data type
% modified 05/22/09     S.M. Rump  improved log10
% modified 08/26/12     S.M. Rump  global variables removed
% modified 10/13/12     S.M. Rump  INTLAB_INTVAL_STDFCTS
%

  e = 1e-30;
  if 1+e==1-e                           % fast check for rounding to nearest
    rndold = 0;
  else
    rndold = getround;
    setround(0)
  end

  N = getappdata(0,'INTLAB_GRADIENT_NUMVAR');

  wng = warning;
  warning off

  % use full(a.x(:)): cures Matlab V6.0 bug
  % a=7; i=[1 1]; x=a(i), b=sparse(a); y=b(i)  yields row vector x but column vector y
  % ax is full anyway
  if isa(a.x,'intval')
    INTLAB_STDFCTS_LOG10_ = getappdata(0,'INTLAB_STDFCTS_LOG10_');
    clog10 = infsup(INTLAB_STDFCTS_LOG10_.INF,INTLAB_STDFCTS_LOG10_.SUP);
    ax = clog10 ./ full(a.x(:));
  else
    ax = 1./( log(10) * full(a.x(:)) );
  end
  a.x = log10(a.x);
  if issparse(a.dx)
    sizeax = size(a.dx,1);
    [ia,ja,sa] = find(a.dx);
    if isa(a.x,'intval')
      adx = times(ax(ia),sa,0);
      if adx.complex
        a.dx = intval( sparse(ia,ja,adx.mid,sizeax,N) , sparse(ia,ja,adx.rad,sizeax,N) , 'midrad' );
      else
        a.dx = intval( sparse(ia,ja,adx.inf,sizeax,N) , sparse(ia,ja,adx.sup,sizeax,N) , 'infsup' );
      end
    else
      a.dx = sparse(ia,ja,ax(ia).*sa,sizeax,N);
    end
  else
    a.dx = a.dx .* ax(:,ones(1,N));
  end

  warning(wng)

  if rndold
    setround(rndold)
  end


function [controlFlux, objFlux] = runRobustnessAnalysis(model, controlRxn, nPoints, objRxn, plotRedCost)
% runRobustnessAnalysis
%   Performs robustness analysis for a reaction of interest and an objective
%   of interest. Modified from the COBRA robustnessAnalysis function.
%
% Input:
%   model           a model structure
%   controlRxn      reaction of interest whose value is to be controlled
%   nPoints         number of points to show on plot (opt, default 20)
%   objRxn          reaction identifier of objective to be maximized (opt,
%                   default it uses the objective defined in the model)
%   plotRedCost     logical whether reduced cost should also be plotted
%                   (opt, default false)
%
% Output:
%   controlFlux     flux values of the reaction of interest, ranging from
%                   its minimum to its maximum value
%   objFlux         optimal values of objective reaction at each control
%                   reaction flux value
%
% Modified from COBRA Toolbox robustnessAnalysis.m
%
% Usage: runRobustnessAnalysis(model, controlRxn, nPoints, objRxn)

if nargin < 3
    nPoints = 20;
end
if nargin < 4
    baseModel = model;
else
    baseModel = setParam(model,'obj',objRxn,1);
end
if nargin < 5
    plotRedCost = false;
end

if any(ismember(model.rxns,controlRxn))
    controlRxnIdx = getIndexes(model,controlRxn,'rxns');
    tmpModel = setParam(model,'obj',controlRxnIdx,1);
    solMax = solveLP(tmpModel);
    solMax = solMax.x(logical(tmpModel.c));
    tmpModel.c = -tmpModel.c;
    solMin = solveLP(tmpModel);
    solMin = solMin.x(logical(tmpModel.c));
else
    error('Control reaction does not exist!');
end

objFlux = zeros(nPoints,1);
redCost = zeros(nPoints,1);
controlFlux = linspace(solMin,solMax,nPoints)';

fprintf('Running robustness analysis...   0%% complete');
for i=1:length(controlFlux)
    progress=pad(num2str(floor(i/numel(controlFlux)*100)),3,'left');
    fprintf('\b\b\b\b\b\b\b\b\b\b\b\b\b%s%% complete',progress);
    modelControlled = setParam(baseModel,'eq',controlRxnIdx,controlFlux(i));
    solControlled = solveLP(modelControlled);
    objFlux(i) = solControlled.x(logical(modelControlled.c));
    redCost(i) = solControlled.rCost(controlRxnIdx);
end
fprintf('\b\b\b\b\b\b\b\b\b\b\b\b\bCOMPLETE\n');

if plotRedCost
    yyaxis right
    plot(controlFlux,redCost)
    ylabel([strrep(controlRxn,'_','-') ' reduced cost']);
    yyaxis left
end
plot(controlFlux,objFlux)
xlabel(strrep(controlRxn,'_','-'));
ylabel('Objective');
end


classdef BMatrixComputer < handle

    properties (Access = private)
        fun
        dNdx
        nVoigt
    end

    methods (Access = public)

        function obj = BMatrixComputer(cParams)
            obj.init(cParams);
        end

        function B = compute(obj,igaus)
            ndimf = obj.fun.ndimf;
            switch ndimf
                case 1
                    obj.nVoigt = 2;
                    B = obj.computeBin1D(igaus);
                case 2
                    obj.nVoigt = 3;
                    B = obj.computeBin2D(igaus);
                case 3
                    obj.nVoigt = 6;
                    B = obj.computeBin3D(igaus);
            end
        end

    end

    methods (Access = private)

        function init(obj, cParams)
            obj.fun  = cParams.fun;
            obj.dNdx = cParams.dNdx;
        end

        function B = computeBin2D(obj,iGaus)
            deriv = obj.dNdx;
            nStre = obj.nVoigt;
            nDimf = obj.fun.ndimf;
            nNodE = size(deriv,2);
            nDofE = nNodE*nDimf;
            nElem = size(deriv,3);
            B = zeros(nStre,nDofE,nElem);
            for iNode = 1:nNodE
                j = nDimf*(iNode-1)+1;
                B(1,j,:)   = deriv(1,iNode,:,iGaus);
                B(2,j+1,:) = deriv(2,iNode,:,iGaus);
                B(3,j,:)   = deriv(2,iNode,:,iGaus);
                B(3,j+1,:) = deriv(1,iNode,:,iGaus);
            end
        end

        function B = computeBin3D(obj,iGaus)
            deriv = obj.dNdx;
            nNode = size(deriv,2);
            nElem = size(deriv,3);
            B = zeros(obj.nVoigt,nNode,nElem);
            for inode = 1:nNode
                j = obj.fun.ndimf*(inode-1)+1;
                % associated to normal strains
                B(1,j,:)   = deriv(1,inode,:,iGaus);
                B(2,j+1,:) = deriv(2,inode,:,iGaus);
                B(3,j+2,:) = deriv(3,inode,:,iGaus);
                % associated to shear strain, gamma12
                B(4,j,:)   = deriv(2,inode,:,iGaus);
                B(4,j+1,:) = deriv(1,inode,:,iGaus);
                % associated to shear strain, gamma13
                B(5,j,:)   = deriv(3,inode,:,iGaus);
                B(5,j+2,:) = deriv(1,inode,:,iGaus);
                % associated to shear strain, gamma23
                B(6,j+1,:) = deriv(3,inode,:,iGaus);
                B(6,j+2,:) = deriv(2,inode,:,iGaus);
            end
        end

        function [B] = computeBin1D(obj, igaus)
            deriv  = obj.dNdx(:,:,:,igaus);
            nDimf = obj.fun.ndimf;
            nNode = size(deriv,2);
            nElem = size(obj.dNdx,3);
            nDofs = nDimf*nNode;
            B = zeros(obj.nVoigt,nDofs,nElem);
            for inode = 1:nNode
                j = nDimf*(inode-1) + 1;
                B(1,j,:) = deriv(1,inode,:);
                B(2,j,:) = deriv(2,inode,:);
            end
        end

    end
end


function [imout,orderout,cimout,meta,thetaout]=scat_display(in,dirac,options,scatt)
%this function constructs the scattering logpolar display, presented
%in the paper 'Invariant Scattering Convolution Networks'.
%scatt=array of scattering coefficients to be displayed
%meta=additional information produced by the scattering function, which encodes the
%scattering path of each coefficient and other necessary information.
%
%options: options struct.
% options.type
%type=0 unified plot: all scattering coefficients are displayed together
%type=2 split plot: it generates a separate output for each scattering order (default option)
%type=1 concatenated plot: it concatenates the coefficients along annular rings.
% options.renorm_process: use dirac renormalisation for the coefficients (default 0)
% options.logpolar: use logpolar display versus cartesian display (default 1)
% options.display_size: size of the scattering display (default 512)
%imout contains the output image.

options.null=0;
use_whole_ener=getoptions(options,'use_whole_ener',0);
type=getoptions(options,'display_type',2);
logpolar=getoptions(options,'display_logpolar',1);
renorm_process=getoptions(options,'renorm_process',0);

maxsize=getoptions(options,'display_size',512);
J=in{1}.meta.j;
L=max(in{2}.meta.theta);

%create the legacy 'meta' structure
meta = recover_meta(in, dirac);
if nargin < 4
scatt = meta.ave;
end

%first_mask=find(meta.order==2);
%J=max(meta.scale(first_mask))+1;
%L=max(meta.orientation(first_mask))+1;

meta.covered=zeros(size(meta.order));
meta=effective_energy(meta,use_whole_ener);
meta=compute_rectangles(meta,use_whole_ener,type==0,logpolar);
maxorder=max(meta.order);

if renorm_process
  norm_ratio = scatt./meta.dirac_norm;
else
  l2_renorm = getoptions(options,'l2_renorm',0);
  denom=ones(size(meta.dirac_norm));
  if l2_renorm
    denom=sqrt(2.^(-mod(meta.scale,J)));
  end
  if (size(scatt)~=size(denom))
    scatt = scatt';
  end
  norm_ratio = scatt./denom;
end

heights=meta.rectangle(:,2)-meta.rectangle(:,1);
widths=meta.rectangle(:,4)-meta.rectangle(:,3);

fact_h = maxsize;
fact_w = maxsize;

if type < 2
  imout=zeros(fact_h+1,fact_w+1);
  orderout=zeros(fact_h+1,fact_w+1);
else
  for m=1:maxorder
    imout{m}=zeros(fact_h,fact_w);
  end
end

if type==1
  %concatenate the rectangles along orders
  %rescale the horizontal coordinate according to total energy of each order
  for ord=1:maxorder
    selected=find(meta.order==ord);
    ordener(ord)=sum(meta.dirac_norm(selected).^2);
  end
  ordener=ordener/sum(ordener);
  cordener=cumsum(ordener);
  cordener=[0 cordener];
  %concatenate and squeeze the rectangles
  lower=0;
  for ord=1:maxorder
    selected=find(meta.order==ord);
    if ordener(ord)>0
      if logpolar
        %obtain the upper and lower bounds of the annulus
        upper=sqrt(lower^2+ordener(ord));
        meta.rectangle(selected,3:4)=sqrt(meta.rectangle(selected,3:4).^2*upper^2+...
          (1-meta.rectangle(selected,3:4).^2)*lower^2);
        lower=upper;
      else
        meta.rectangle(selected,3:4)=ordener(ord)*meta.rectangle(selected,3:4)+cordener(ord);
      end
    end
  end
  type=0;
end

switch type
  case 0
    for l=1:length(norm_ratio)
      %extrema
      ext(1)=1+floor(fact_h*meta.rectangle(l,1));
      ext(2)=1+floor(fact_h*meta.rectangle(l,2));
      ext(3)=1+floor(fact_w*meta.rectangle(l,3));
      ext(4)=1+floor(fact_w*meta.rectangle(l,4));

      inthh=[ext(1):ext(2)];
      intww=[ext(3):ext(4)];
      imout(inthh,intww)=norm_ratio(l);
      orderout(inthh,intww)=meta.order(l);

    end
    if logpolar
      [imout,thetaout]=logpolar_conversion(imout,L);
      [orderout]=logpolar_conversion(orderout,L);
    end
    m1=(orderout==2);
    m2=(orderout==3);
    m3=(orderout>3);
    [gox,goy]=gradient(orderout);
    couronnes=(conv2(gox.^2+goy.^2,ones(5),'same') < .25);
    nimout=imout/max(imout(:));
    nimout=nimout.*couronnes+(1-couronnes);
    [NN,MM]=size(nimout);
    cimout=ones(NN,MM,3);
    cimout(:,:,1)=1-nimout.*(m2|m3);
    cimout(:,:,3)=1-nimout.*(m1|m3);
    cimout(:,:,2)=1-nimout.*(m1|m2);

  case 2
    for ord=2:maxorder
      selected=find(meta.order==ord);
      for l=selected
        inth=min(fact_h,[1+floor(fact_h*meta.rectangle(l,1)):floor(fact_h*meta.rectangle(l,2))]);
        intw=min(fact_w,[1+floor(fact_w*meta.rectangle(l,3)):floor(fact_w*meta.rectangle(l,4))]);
        imout{ord-1}(inth,intw)=norm_ratio(l);
      end
      if logpolar
        [imout{ord-1}]=logpolar_conversion(imout{ord-1},L);
      end
    end

end

end

function meta=compute_rectangles(meta,use_whole_ener,unified_plot, logpolar)

LP_correction=1;

R=length(meta.order);
maxorder=max(meta.order);
first_mask=find(meta.order==2);
J=max(meta.scale(first_mask))+1;
L=max(meta.orientation(first_mask))+1;

meta.lp_correction=1;%min(1,meta.lp_correction);
meta.rectangle(1,:)=[0 1 0 sqrt(meta.lp_correction)];
%meta.dirac_onorm(1)=meta.dirac_onorm(1)*LP_correction;
meta.dirac_onorm=meta.dirac_norm;

for o=1:maxorder-1
  slice=find(meta.order==o);
  for s=slice
    %find children
    if o==1
      children=find(meta.order==o+1);
    else
      children=find((floor(meta.scale/J)==meta.scale(s))&(floor(meta.orientation/L)==meta.orientation(s))&(meta.order==o+1));
    end
    if ~isempty(children)
      [newrectangles,outrect]=split_rectangle(meta.rectangle(s,:),meta.scale(children),...
        meta.orientation(children),meta.dirac_norm(s),meta.dirac_onorm(s),...
        meta.dirac_effnorms(children),J,L,use_whole_ener,unified_plot,logpolar,o);
      for c=1:length(children)
        meta.rectangle(children(c),:)=newrectangles(c,:);
      end
      meta.covered(s) = ((outrect(2)-outrect(1))*(outrect(4)-outrect(3))+...
        sum((newrectangles(:,2)-newrectangles(:,1)).*(newrectangles(:,4)-newrectangles(:,3))))/...
        ((meta.rectangle(s,2)-meta.rectangle(s,1))*(meta.rectangle(s,4)-meta.rectangle(s,3)));
      meta.rectangle(s,:)=outrect;
    end
  end
end

end

function [out,outlowp]=split_rectangle(inrectangle, scales, orientations, dirac_phi, dirac_orig,dirac_norms,J,L,use_whole_ener,unified_plot, logpolar,order)

%first step: we marginalize orientations in order to split scale axis:
C=length(dirac_norms);
if unified_plot | order==1
  ener=dirac_phi^2;
  totener=sum(dirac_norms)+ener;
else
  totener=sum(dirac_norms);
end
if use_whole_ener
  totener=dirac_orig^2;
end

totalheight=inrectangle(2)-inrectangle(1);
if logpolar
  totalwidth=inrectangle(end)^2-inrectangle(end-1)^2;
else
  totalwidth=inrectangle(end)-inrectangle(end-1);
end

outlowp=inrectangle;
if unified_plot | order==1
  if logpolar
    outlowp(end)=sqrt(outlowp(end-1)^2+totalwidth*(ener/totener));
  else
    outlowp(end)=outlowp(end-1)+totalwidth*(ener/totener);
  end
  rasterwidth=outlowp(end);
else
  rasterwidth=inrectangle(3);
end
scale_parent= mod(floor(scales/J),J);
orient_parent=mod(floor(orientations/L),L);
diffori=1;

for j=J-1:-1:0
  pack=find(mod(scales,J)==j);
  if ~isempty(pack)
    width=sum(dirac_norms(pack).^1);
    rasterheight=inrectangle(1);
    for l=0:L-1
      ind=find(mod(orientations(pack)-diffori*orient_parent(pack)+(order>1)*L/2,L)==l);
      out(pack(ind),1)=rasterheight;
      out(pack(ind),2)=rasterheight+totalheight*dirac_norms(pack(ind)).^1/width;
      out(pack(ind),3)=rasterwidth;
      if logpolar
        out(pack(ind),4)=sqrt(rasterwidth^2+totalwidth*width/totener);
      else
        out(pack(ind),4)=rasterwidth+totalwidth*width/totener;
      end
      rasterheight=out(pack(ind),2);
    end
    if logpolar
      rasterwidth=sqrt(rasterwidth^2+totalwidth*width/totener);
    else
      rasterwidth=rasterwidth+totalwidth*width/totener;
    end
  end
end

if order==3 & ~use_whole_ener
  %sanity check: conservation of energy
  in_area = totalheight*totalwidth;
  if unified_plot
    if logpolar
      out_area = (outlowp(2)-outlowp(1))*(outlowp(4)^2-outlowp(3)^2) + sum( (out(:,2)-out(:,1)).*(out(:,4).^2-out(:,3).^2));
    else
      out_area = (outlowp(2)-outlowp(1))*(outlowp(4)-outlowp(3)) + sum( (out(:,2)-out(:,1)).*(out(:,4)-out(:,3)));
    end
  else
    if logpolar
      out_area =  sum( (out(:,2)-out(:,1)).*(out(:,4).^2-out(:,3).^2));
    else
      out_area =  sum( (out(:,2)-out(:,1)).*(out(:,4)-out(:,3)));
    end
  end

  tol=1e-5;
  if abs(in_area-out_area) > tol*in_area
    in_area
    out_area
    out
    inrectangle
    error('sthg weird')
  end
end

end

function metaout=effective_energy(meta,use_whole_ener)

R=length(meta.order);
maxorder=max(meta.order);
first_mask=find(meta.order==2);
J=max(meta.scale(first_mask))+1;
L=max(meta.orientation(first_mask))+1;

last_mask=find(meta.order==maxorder);
metaout=meta;
metaout.dirac_effnorms(last_mask)=meta.dirac_norm(last_mask).^2;
if use_whole_ener
  metaout.dirac_effnorms(last_mask)=meta.dirac_onorm(last_mask).^2;
end

for o=maxorder-1:-1:1
  slice=find(meta.order==o);
  for s=slice
    %find children
    if o==1
      children=find(meta.order==o+1);
    else
      children=find((floor(meta.scale/J)==meta.scale(s))&(floor(meta.orientation/L)==meta.orientation(s))&(meta.order==o+1));
    end
    metaout.dirac_effnorms(s)=sum(metaout.dirac_effnorms(children))+meta.dirac_norm(s).^2;
    if use_whole_ener
      metaout.dirac_effnorms(s)=metaout.dirac_onorm(s).^2;
    end
  end
end

end

function [out,theta]=logpolar_conversion(in,L)

%out=in;
%theta=in;
%return;

[N,M]=size(in);

ix=-M:M;
iix=ones(length(ix),1)*ix;
iiy=iix';
r=sqrt(iix.^2+iiy.^2);
theta=angle(iiy+i*iix);

[N2,M2]=size(r);
%r=r(:,round(M2/2):end);
mask=(r>M);
%theta=theta(:,round(M2/2):end);
theta=mod(theta+(0*L+1)*pi/(2*L),pi);
theta=theta/pi;

code=min(N-2,max(1,round(theta*N)))+N*min(M-1,max(1,round(r)));
out=in(max(1,min(numel(in),1+code)));
out(mask)=0;
clear theta;
theta(:,:,1)=mod(code+1,N);
theta(:,:,2)=min(M-1,max(1,round(r)));

end


function ierror = part_sf_check ( n, npart, a )

%*****************************************************************************80
%
%% PART_SF_CHECK checks a standard form partition of an integer.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    25 January 2011
%
%  Author:
%
%    John Burkardt
%
%  Reference:
%
%    Donald Kreher, Douglas Simpson,
%    Combinatorial Algorithms,
%    CRC Press, 1998,
%    ISBN: 0-8493-3988-X,
%    LC: QA164.K73.
%
%  Parameters:
%
%    Input, integer N, the integer to be partitioned.
%    N must be positive.
%
%    Input, integer NPART, the number of parts of the partition.
%    1 <= NPART <= N.
%
%    Input, integer A(NPART), contains the partition.
%    A(1) through A(NPART) contain the nonzero integers which
%    sum to N.  The entries must be in DESCENDING order.
%
%    Output, integer IERROR, error flag.
%    0, no error.
%    -1, N is illegal.
%    -2, NPART is illegal.
%    -3, the entries do not add up to N.
%    I, the I-th entry of A is illegal.
%
  ierror = 0;

  if ( n < 1 )
    ierror = -1;
    return
  end

  if ( npart < 1 || n < npart )
    ierror = -2;
    return
  end
%
%  Every entry must lie between 1 and N.
%
  for i = 1 : npart
    if ( a(i) < 1 || n < a(i) )
      ierror = i;
      return
    end
  end
%
%  The entries must be in descending order.
%
  for i = 2 : npart
    if ( a(i-1) < a(i) )
      ierror = i;
      return
    end
  end
%
%  The entries must add up to N.
%
  if ( sum ( a(1:npart) ) ~= n )
    ierror = -3;
  end

  return
end


classdef LMOCSO < ALGORITHM
% <multi/many> <real/integer> <large/none> <constrained/none>
% Large-scale multi-objective competitive swarm optimization algorithm

%------------------------------- Reference --------------------------------
% Y. Tian, X. Zheng, X. Zhang, and Y. Jin, Efficient large-scale multi-
% objective optimization based on a competitive swarm optimizer, IEEE
% Transactions on Cybernetics, 2020, 50(8): 3696-3708.
%------------------------------- Copyright --------------------------------
% Copyright (c) 2023 BIMK Group. You are free to use the PlatEMO for
% research purposes. All publications which use this platform or any code
% in the platform should acknowledge the use of "PlatEMO" and reference "Ye
% Tian, Ran Cheng, Xingyi Zhang, and Yaochu Jin, PlatEMO: A MATLAB platform
% for evolutionary multi-objective optimization [educational forum], IEEE
% Computational Intelligence Magazine, 2017, 12(4): 73-87".
%--------------------------------------------------------------------------

    methods
        function main(Algorithm,Problem)
            %% Generate random population
            [V,Problem.N] = UniformPoint(Problem.N,Problem.M);
            Population    = Problem.Initialization();
            Population    = EnvironmentalSelection(Population,V,(Problem.FE/Problem.maxFE)^2);

            %% Optimization
            while Algorithm.NotTerminated(Population)
                Fitness = calFitness(Population.objs);
                if length(Population) >= 2
                    Rank = randperm(length(Population),floor(length(Population)/2)*2);
                else
                    Rank = [1,1];
                end
                Loser  = Rank(1:end/2);
                Winner = Rank(end/2+1:end);
                Change = Fitness(Loser) >= Fitness(Winner);
                Temp   = Winner(Change);
                Winner(Change) = Loser(Change);
                Loser(Change)  = Temp;
                Offspring      = Operator(Problem,Population(Loser),Population(Winner));
                Population     = EnvironmentalSelection([Population,Offspring],V,(Problem.FE/Problem.maxFE)^2);
            end
        end
    end
end

function Fitness = calFitness(PopObj)
% Calculate the fitness by shift-based density

    N      = size(PopObj,1);
    fmax   = max(PopObj,[],1);
    fmin   = min(PopObj,[],1);
    PopObj = (PopObj-repmat(fmin,N,1))./repmat(fmax-fmin,N,1);
    Dis    = inf(N);
    for i = 1 : N
        SPopObj = max(PopObj,repmat(PopObj(i,:),N,1));
        for j = [1:i-1,i+1:N]
            Dis(i,j) = norm(PopObj(i,:)-SPopObj(j,:));
        end
    end
    Fitness = min(Dis,[],2);
end