function rv = radvl (pos, vel, velobs, star, dist)

% this function predicts the radial velocity of the observed
% object as it would be measured by spectroscopic means.  radial
% velocity is here defined as the radial velocity measure (z)
% times the speed of light.  for a solar system body, it applies
% to a fictitious emitter at the center of the observed object,
% assumed massless (no gravitational red shift), and does not
% in general apply to reflected light.  for stars, it includes
% all effects, such as gravitational red shift, contained
% in the catalog barycentric radial velocity measure, a scalar
% derived from spectroscopy.  nearby stars with a known kinematic
% velocity vector (obtained independently of spectroscopy) can be
% treated like solar system objects.  see lindegren & dravins
% (2003), astronomy & astrophysics 401, 1185-1201.
%
%      pos    = geometric position vector of object with respect to
%               observer, corrected for light-time, in au (in)
%      vel    = velocity vector of object with respect to solar
%               system barycenter, components in au/day (in)
%      velobs = velocity vector of observer with respect to solar
%               system barycenter, components in au/day (in)
%      star   = 3-element array of catalog data for a star, to be
%               non-zero if observed object is a star for which the
%               catalog radial velocity is consistent with
%               the iau definition of barycentric radial velocity
%               measure (otherwise all elements should be set to
%               0.d0 exactly) (in)
%               star(1) = catalog ra in hours
%               star(2) = catalog dec in degrees
%               star(3) = z*c, the catalog barycentric radial
%                         velocity measure times the speed of light,
%                         in kilometers/second
%               all three data elements must apply to the same
%               epoch (usually j2000.0 = jd 2451545.0 tt)
%      dist   = 3-element array of distances in au (in)
%               dist(1) = distance of observer from the geocenter
%               dist(2) = distance of observer from the sun
%               dist(3) = distance of object from the sun
%      rv     = the observed radial velocity measure times
%               the speed of light, in kilometers/second (out)

% note 1:  all the input arguments are bcrs quantities, expressed
% with respect to the icrs axes.  vel and velobs are kinematic
% velocities -- derived from geometry or dynamics, not spectroscopy.

% note 2:  if any element of array star is non-zero, the algorithm
% used will be consistent with the iau definition of stellar
% radial velocity, specifically, the barycentric radial velocity
% measure, which is derived from spectroscopy.  in that case,
% the vector vel can be very approximate -- or, for distant stars
% or galaxies, zero -- as it will be used only for a small geometric
% correction that is proportional to proper motion.

% note 3:  any of the distances in array dist can be set to zero
% (0.d0) if the corresponding general relativistic gravitational
% potential term is not to be evaluated.  these terms
% generally are important only at the meter/second level.  if
% the first two distances are both zero, an average value
% will be used for the relativistic term for the observer,
% appropriate for an observer on the surface of the earth.  the
% third distance, if given, is used only for solar system objects.

% ported from NOVAS 3.1

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

radcon = pi / 180.0d0;

% get au, length of astronomical unit in meters

au = astcon ('au', 1.0d0);

% get c, the speed of light in meters/second

c = astcon ('c', 1.0d0);

% get gs, heliocentric gravitational constant

gs = astcon ('gs', 1.0d0);

% get ge, geocentric gravitational constant

ge = astcon ('ge', 1.0d0);

% (gs and ge are in meters^3/second^2)

c2 = c^2;

toms = au / 86400.0d0;

toms2 = toms^2;

rv = 0.0d0;

% compute length of position vector = distance to object in au

posmag = sqrt(pos(1)^2 + pos(2)^2 + pos(3)^2);

if (posmag < 1.0d-8)

    return

end

% determine how object is to be processed

dostar = star(1) ~= 0.0d0 || star(2) ~= 0.0d0 || star(3) ~= 0.0d0;

% compute unit vector toward object

for j = 1:3

    uk(j) = pos(j) / posmag;

end

% compute velocity-squared factors

v2 = (vel(1)^2 + vel(2)^2 + vel(3)^2) * toms2;

vo2 = (velobs(1)^2 + velobs(2)^2 + velobs(3)^2) * toms2;

% compute geopotential at observer, unless observer is geocentric

r = dist(1) * au;

phigeo = 0.0d0;

if (r > 1.0d6)

    phigeo = ge / r;

end

% compute solar potential at observer

r = dist(2) * au;

phisun = 0.0d0;

if (r > 1.0d8)

    phisun = gs / r;

end

% compute relativistic potential and velocity factor for observer

if (dist(1) ~= 0.0d0 || dist(2) ~= 0.0d0)

    % lindegren & dravins eq. (41), second factor in parentheses

    rel = 1.0d0 - (phigeo + phisun) / c2 - 0.5d0 * vo2 / c2;

else

    % lindegren & dravins eq. (42), inverse

    rel = 1.0d0 - 1.550d-8;

end

if (dostar == 1)

    % for stars, update barycentric radial velocity measure for change in view angle

    ra = star(1) * 15.0d0 * radcon;

    dc = star(2) * radcon;

    du(1) = uk(1) - (cos (dc) * cos (ra));

    du(2) = uk(2) - (cos (dc) * sin (ra));

    du(3) = uk(3) - (sin (dc));

    zc = star(3) * 1.0d3 + (vel(1) * du(1) + vel(2) * du(2) + vel(3) * du(3)) * toms;

    % compute observed radial velocity measure of a star (inverse of
    % lindegren & dravins eq. (41))

    zb1 = 1.d0 + zc / c;

    kvobs = (uk(1) * velobs(1) + uk(2) * velobs(2) + uk(3) * velobs(3)) * toms;

    zobs1 = zb1 * rel / (1.0d0 + kvobs / c);

else

    % compute solar potential at object, if within solar system

    r = dist(3) * au;

    phisun = 0.0d0;

    if (r > 1.0d8 && r < 1.0d16)

        phisun = gs / r;

    end

    % compute observed radial velocity measure of a planet or other
    % object -- including a nearby star -- where kinematic
    % barycentric velocity vector is known and gravitational
    % red shift is negligible (lindegren & dravins eq. (40),
    % applied as per s. klioner private communication (2006))

    kv = (uk(1) * vel(1) + uk(2) * vel(2) + uk(3) * vel(3)) * toms;

    zb1 = (1.0d0 + kv / c) / (10.0d0 - phisun / c2 - 0.5d0 * v2 / c2);

    kvobs = (uk(1) * velobs(1) + uk(2) * velobs(2) + uk(3) * velobs(3)) * toms;

    zobs1 = zb1 * rel / (1.0d0 + kvobs / c);

end

% convert observed radial velocity measure to kilometers/second

rv = (zobs1 - 1.0d0) * c / 1000.0d0;


function [c, s, b, g, active_set] = foopsi_oasisAR1(y, g, lam, smin, optimize_b,...
    optimize_g, decimate, maxIter, tau_range, gmax)
%% Infer the most likely discretized spike train underlying an AR(1) fluorescence trace
% Solves the sparse non-negative deconvolution problem
%  min 1/2|c-y|^2 + lam |s|_1 subject to s_t = c_t-g c_{t-1} >= 0

%% inputs:
%   y:  T*1 vector, One dimensional array containing the fluorescence intensities
%withone entry per time-bin.
%   g:  scalar, Parameter of the AR(1) process that models the fluorescence ...
%impulse response.
%   lam:  scalar, sparsity penalty parameter
%   optimize_b: bool, optimize baseline if True
%   optimize_g: integer, number of large, isolated events to consider for
%       optimizing g
%   decimate: int, decimation factor for estimating hyper-parameters faster
%       on decimated data
%   maxIter:  int, maximum number of iterations
%   active_set: npool x 4 matrix, warm stared active sets
%   gmax:  scalar, maximum value of g
%   tau_range: [tau_min, tau_max]
%% outputs
%   c: T*1 vector, the inferred denoised fluorescence signal at each time-bin.
%   s: T*1 vector, discetized deconvolved neural activity (spikes)
%   b: scalar, fluorescence baseline
%   g: scalar, parameter of the AR(1) process
%   active_set: npool x 4 matrix, active sets

%% Authors: Pengcheng Zhou, Carnegie Mellon University, 2016
% ported from the Python implementation from Johannes Friedrich

%% References
% Friedrich J et.al., NIPS 2016, Fast Active Set Method for Online Spike Inference from Calcium Imaging

%% input arguments
y = reshape(y, [], 1);
T = length(y);

if ~exist('g', 'var') || isempty(g)
    g = estimate_time_constant(y, 1);
end
if ~exist('lam', 'var') || isempty(lam);   lam = 0; end
if ~exist('smin', 'var') || isempty(smin)
    smin = 0;
elseif smin<0
    smin = abs(smin) * GetSn(y);     % use a threshold that is proportional to the noise level
end
if ~exist('optimize_b', 'var') || isempty(optimize_b)
    optimize_b = false;
end
if ~exist('optimize_g', 'var') || isempty(optimize_g)
    optimize_g = 0;
end
if ~exist('decimate', 'var') || isempty(decimate)
    decimate = 1;
else
    decimate = max(1, round(decimate));
end
if ~exist('maxIter', 'var') || isempty(maxIter)
    maxIter = 10;
end
if ~exist('tau_range', 'var') || isempty(tau_range)
   g_range = [0, 1];
else
   g_range = exp(-1./tau_range);
   g = min(max(g, g_range(1)), g_range(2));
end

% change parameters due to downsampling
if decimate>1
    decimate = 1;  %#ok<NASGU>
    disp('to be done');
    %     fluo = y;
    %     y = resample(y, 1, decimate);
    %     g = g^decimate;
    %     thresh = thresh / decimate / decimate;
    %     T = length(y);
end

%% optimize parameters
if ~optimize_b   %% don't optimize the baseline b
    %% initialization
    b = 0;
    [solution, spks, active_set] = oasisAR1(y, g, lam, smin);

    %% iteratively update parameters g
    if  optimize_g     % update g
        [solution, active_set, g, spks] = update_g(y, active_set,lam, smin, g_range);
    end
else
    %% initialization
    b = quantile(y, 0.15);
    [solution, spks, active_set] = oasisAR1(y-b, g, lam, smin);

    %% iteratively update parameters g and b
    for m=1:maxIter
        b = mean(y-solution);
        if  optimize_g     % update g
            if size(active_set, 1) == 0
                break;
            end
            g0 = g;
            if g>gmax  % spike counts are too small. stop
                g = estimate_time_constant(y, 1);
                [solution, spks, active_set] = oasisAR1(y-b, g, lam, smin);
                break;
            end
            [solution, active_set, g, spks] = update_g(y-b, active_set,lam, smin, g_range);
            if abs(g-g0)/g0 < 1e-3  % g is converged
                optimize_g = false;
            end
        else
            break;
        end
    end
end
c = solution;
s = spks;
end
%update the AR coefficient: g
function [c, active_set, g, s] = update_g(y, active_set, lam, smin, g_range)
%% inputs:
%   y:  T*1 vector, One dimensional array containing the fluorescence intensities
%withone entry per time-bin.
%   active_set: npools*4 matrix, previous active sets.
%   lam:  scalar, curret value of sparsity penalty parameter lambda.

%% outputs
%   c: T*1 vector
%   s: T*1 vector, spike train
%   active_set: npool x 4 matrix, active sets
%   g: scalar

%% Authors: Pengcheng Zhou, Carnegie Mellon University, 2016
% ported from the Python implementation from Johannes Friedrich

%% References
% Friedrich J et.al., NIPS 2016, Fast Active Set Method for Online Spike Inference from Calcium Imaging

%% initialization
if ~exist('g_range', 'var') || isempty(g_range)
   g_range = [0, 1];
end
len_active_set = size(active_set, 1);  %number of active sets
y = reshape(y,[],1);    % fluorescence data
maxl = max(active_set(:, 4));   % maximum ISI
c = zeros(size(y));     % the optimal denoised trace

%% find the optimal g and get the warm started active_set
h = [];
g = fminbnd(@rss_g, g_range(1), g_range(2));
yp = y - lam*(1-g);
for m=1:len_active_set
    tmp_h = exp(log(g)*(0:maxl)');   % response kernel
    tmp_hh = cumsum(h.*h);        % hh(k) = h(1:k)'*h(1:k)
    li = active_set(m, 4);
    ti = active_set(m, 3);
    idx = ti:(ti+li-1);
    active_set(m,1) = (yp(idx))'*tmp_h(1:li);
    active_set(m,2) = tmp_hh(li);
end
[c,s,active_set] = oasisAR1(y, g, lam, smin, active_set);

%% nested functions
    function rss = rss_g(g)
        h = exp(log(g)*(0:maxl)');   % response kernel
        hh = cumsum(h.*h);        % hh(k) = h(1:k)'*h(1:k)
        yp = y - lam*(1-g);     % include the penalty term
        for ii=1:len_active_set
            li = active_set(ii, 4);
            ti = active_set(ii, 3);
            idx = ti:(ti+li-1);
            tmp_v = max(yp(idx)' * h(1:li) / hh(li), 0);
            c(idx) = tmp_v*h(1:li);
        end
        res = y-c;
        rss = res'*res;     % residual sum of squares
    end
end


function [unionVolume,intersectVolume] = calcUnionIntersectionVol(structNumV,planC)
%function planC = calcUnionIntersectionVol(structNumV,planC)
%
%This function calculates volume of union and intersection from structNumV.
%
%APA, 01/25/08
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
% CERR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
% without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
% See the GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with CERR.  If not, see <http://www.gnu.org/licenses/>.

global stateS

if ~exist('planC')
    global planC
end

indexS = planC{end};

scanNum = getStructureAssociatedScan(structNumV(1),planC);
structureC{1} = getUniformStr(structNumV(1));
%Get z-coordinates of scan
[scanXv,scanYv,scanZv] = getUniformScanXYZVals(planC{indexS.scan}(scanNum));
[numRows,numCols,numSlcs] = size(structureC{1});
zeroSlcM = zeros(numRows,numCols,'uint8');

for strInd = 2:length(structNumV)

    structNum = structNumV(strInd);

    %return if structNum is already associated to scanNum
    assocScanNum = getAssociatedScan(planC{indexS.structures}(structNum).assocScanUID,planC);

    if scanNum ~= assocScanNum
        struct3M = [];

        %Get transformation matrix
        if ~isfield(planC{indexS.scan}(scanNum),'transM') | isempty(planC{indexS.scan}(scanNum).transM)
            transMnew = eye(4);
        else
            transMnew = planC{indexS.scan}(scanNum).transM;
        end
        if ~isfield(planC{indexS.scan}(assocScanNum),'transM') | isempty(planC{indexS.scan}(assocScanNum).transM)
            transMold = eye(4);
        else
            transMold = planC{indexS.scan}(assocScanNum).transM;
        end
        transM = inv(transMnew)*transMold;

        [jnk, relStructNum] = getStructureAssociatedScan(structNum, planC);

        h = waitbar(0,'Interpolating scan...');

        for i = 1:length(scanZv)
            coord = scanZv(i);

            %structUID   = planC{indexS.structures}(structNum).strUID;
            %contourS    = calllib('libMeshContour','getContours',structUID,single(pointOnPlane),single(planeNormal),single([0 1 0]),single([1 0 0]));

            [slc, sliceXVals, sliceYVals] = getStructureSlice(assocScanNum, 3, coord, transM, planC);
            if (length(sliceXVals) ~= numCols) || (length(sliceYVals) ~= numRows)
                error('Associated scans have different size')
            end
            oneStructM  = double(bitget(slc, relStructNum));
            if ~isempty(oneStructM)
                struct3M(:,:,i) = oneStructM';
            else
                struct3M(:,:,i) = zeroSlcM;
            end
            waitbar(i/length(scanZv),h)

        end
        close(h)
        structureC{strInd} = struct3M;

    else % structure associated to same base scan

        structureC{strInd} = getUniformStr(structNum, planC);

    end

end

combineStr3M = structureC{1};
for i=2:length(structureC)
    combineStr3M = combineStr3M + structureC{i};
end
intersectStr3M = combineStr3M==length(structureC);
unionStr3M = combineStr3M>0;

dx = abs(scanXv(1)-scanXv(2));
dy = abs(scanYv(1)-scanYv(2));
dz = abs(scanZv(1)-scanZv(2));

unionVolume = length(find(unionStr3M)) * dx*dy*dz;
intersectVolume = length(find(intersectStr3M)) * dx*dy*dz;

nameStr = '';
for i=1:length(structNumV)
    nameStr = [nameStr , ', ', planC{indexS.structures}(structNumV(i)).structureName];
end
nameStr(1) = [];
nameStr = ['Structures:', nameStr];

CERRStatusString('----------------------------------------------------------','console')
CERRStatusString(nameStr,'console')
CERRStatusString(['Volume of Union = ',num2str(unionVolume),' cc'],'console')
CERRStatusString(['Volume of Intersection = ',num2str(intersectVolume), ' cc'],'console')
CERRStatusString(['Intersection/Union = ',num2str(intersectVolume/unionVolume)],'console')
CERRStatusString('----------------------------------------------------------','console')
CERRStatusString(['Intrsc = ',num2str(intersectVolume), ',  Union = ',num2str(unionVolume), ' cc'],'gui')

return;


%POLYGON Polygon class
%
% A general class for manipulating polygons and vectors of polygons.
%
% Methods::
%  plot           Plot polygon
%  area           Area of polygon
%  moments        Moments of polygon
%  centroid       Centroid of polygon
%  perimeter      Perimter of polygon
%  transform      Transform polygon
%  inside         Test if points are inside polygon
%  intersection   Intersection of two polygons
%  difference     Difference of two polygons
%  union          Union of two polygons
%  xor            Exclusive or of two polygons
%  display        print the polygon in human readable form
%  char           convert the polgyon to human readable string
%
% Properties::
%  vertices   List of polygon vertices, one per column
%  extent     Bounding box [minx maxx; miny maxy]
%  n          Number of vertices
%
% Notes::
% - This is reference class object
% - Polygon objects can be used in vectors and arrays
%
% Acknowledgement::
%
% The methods: inside, intersection, difference, union, and xor are based on code
% written by:
%  Kirill K. Pankratov, kirill@plume.mit.edu,
%  http://puddle.mit.edu/~glenn/kirill/saga.html
% and require a licence.  However the author does not respond to email regarding
% the licence, so use with care, and modify with acknowledgement.

% Copyright (C) 1993-2014, by Peter I. Corke
%
% This file is part of The Robotics Toolbox for MATLAB (RTB).
%
% RTB is free software: you can redistribute it and/or modify
% it under the terms of the GNU Lesser General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% RTB is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU Lesser General Public License for more details.
%
% You should have received a copy of the GNU Leser General Public License
% along with RTB.  If not, see <http://www.gnu.org/licenses/>.
%
% http://www.petercorke.com

% TODO
%  split the code in two.  Simple polygon functions in Polgon class, subclass with
%  Pankratov code to Polygon2.
%  add method to detect empty polygon, overload isempty

classdef Polygon < handle

    properties
        vertices
        extent
    end

    properties (Dependent=true)
        n
        x
        y
    end

    methods

        function p = Polygon(v, wh)
            %Polygon.Polygon Polygon class constructor
            %
            % P = Polygon(V) is a polygon with vertices given by V, one column per
            % vertex.
            %
            % P = Polygon(C, WH) is a rectangle centred at C with dimensions
            % WH=[WIDTH, HEIGHT].

            if nargin == 0
                p.n = 0;
                p.vertices = [];
                return;
            end

            if nargin < 2
                if numrows(v) ~= 2
                    error('vertices must have two rows');
                end
                p.vertices = v;
            end
            if nargin == 2
                if length(v) ~= 2
                    error('first argument must be polygon centre');
                end
                if length(wh) ~= 2
                    error('second arugment must be width height');
                end

                p.vertices = [
                    v(1)-wh(1)/2  v(1)+wh(1)/2  v(1)+wh(1)/2  v(1)-wh(1)/2
                    v(2)-wh(2)/2  v(2)-wh(2)/2  v(2)+wh(2)/2 v(2)+wh(2)/2 ];
            end

            % compute the extent
            p.extent(1,1) = min(p.x);
            p.extent(1,2) = max(p.x);
            p.extent(2,1) = min(p.y);
            p.extent(2,2) = max(p.y);
        end

        function r = get.n(p)
            r = numcols(p.vertices);
        end

        function r = get.x(p)
            r = p.vertices(1,:)';
        end

        function r = get.y(p)
            r = p.vertices(2,:)';
        end

        function r = set.n(p)
            error('cant set property');
        end
        function r = set.x(p)
            error('cant set property');
        end
        function r = set.y(p)
            error('cant set property');
        end

        function s = char(p)
            %Polygon.char String representation
            %
            % S = P.char() is a compact representation of the polgyon in human
            % readable form.
            s = sprintf('%d vertices', p.n);
        end

        function display(p)
            %Polygon.display Display polygon
            %
            % P.display() displays the polygon in a compact human readable form.
            %
            % See also Polygon.char.
            loose = strcmp( get(0, 'FormatSpacing'), 'loose');
            if loose
                disp(' ');
            end
            disp([inputname(1), ' = '])
            if loose
                disp(' ');
            end
            disp(char(p))
            if loose
                disp(' ');
            end
        end

        function plot(plist, varargin)
            %Polygon.plot Draw polygon
            %
            % P.plot() draws the polygon P in the current plot.
            %
            % P.plot(LS) as above but pass the arguments LS to plot.
            %
            % Notes::
            % - The polygon is added to the current plot.

            opt.fill = [];
            [opt,args] = tb_optparse(opt, varargin);

            ish = ishold
            hold all

            for p=plist
                % for every polygon in the list

                % get the vertices
                X = p.vertices(1,:)';
                Y = p.vertices(2,:)';

                while true

                    % look for NaNs which indicate disjoint vertex sets
                    k = find(isnan(X));

                    if length(k) > 0
                        % if a NaN chop out the segment before and after
                        k = k(1);
                        x = X(1:k-1);
                        y = Y(1:k-1);
                        X = X(k+1:end);
                        Y = Y(k+1:end);
                    else
                        x = X; y = Y;
                    end

                    % close the polygon
                    x = [x; x(1)];
                    y = [y; y(1)];

                    if opt.fill
                        patch(x, y, opt.fill);
                    else
                        plot(x, y, args{:});
                    end

                    if length(k) == 0
                        break;
                    end
                end
            end
            hold(ish)
        end

        function a = area(p)
            %Polygon.area Area of polygon
            %
            % A = P.area() is the area of the polygon.
            %
            % See also Polygon.moments.
            a = p.moments(0, 0);
        end

        function m = moments(p, mp, mq)
            %Polygon.moments Moments of polygon
            %
            % A = P.moments(p, q) is the pq'th moment of the polygon.
            %
            % See also Polygon.area, Polygon.centroid, mpq_poly.
            m = mpq_poly(p.vertices, mp, mq);
        end

        function q = transform(p, T)
            %Polygon.transform Transform polygon vertices
            %
            % P2 = P.transform(T) is a new Polygon object whose vertices have
            % been transformed by the SE(2) homgoeneous transformation T (3x3).
            if length(T) == 3
                T = se2(T);
            end
            q = Polygon( homtrans(T, p.vertices) );
        end

        function f = inside(p, points)
            %Polygon.inside Test if points are inside polygon
            %
            % IN = P.inside(P) tests if points given by columns of P (2xN) are inside
            % the polygon.  The corresponding elements of IN (1xN) are either true or
            % false.
            IN = inpolygon(points(1,:), points(2,:), p.x, p.y)
        end

        function c = centroid(p)
            %Polygon.centroid Centroid of polygon
            %
            % X = P.centroid() is the centroid of the polygon.
            %
            % See also Polygon.moments.
            xc = p.moments(1,1) / p.area();
        end

        function r = perimeter(p)
            %Polygon.perimeter Perimeter of polygon
            %
            % L = P.perimeter() is the perimeter of the polygon.
            p = sum(sqrt(diff(p.x).^2+diff(p.y).^2));
        end

        function f = intersect(p, plist)
            %Polygon.intersect Intersection of polygon with list of polygons
            %
            % I = P.intersect(PLIST) indicates whether or not the Polygon P
            % intersects with
            %
            % i(j) = 1 if p intersects polylist(j), else 0.

            % Based on ISINTPL
            %  Copyright (c) 1995 by Kirill K. Pankratov,
            %       kirill@plume.mit.edu.
            %       06/20/95, 08/25/95
            f = [];
            for q=plist
                f = [f isintpl(p.x, p.y, q.x, q.y)];
            end
        end

        function f = intersect_line(p, l)
            %Polygon.intersect_line Intersection of polygon and line segment
            %
            % I = P.intersect_line(L) is the intersection points of a polygon P with
            % the line segment L=[x1 x2; y1 y2].  I (2xN) has one column per
            % intersection, each column is [x y]'.

            f = [];
            % find intersections
            for i=1:p.n
                in = mod(i, p.n)+1;

                xv = [p.x(i); p.x(in)];
                yv = [p.y(i); p.y(in)];
                intsec = iscross(xv, yv, l(1,:)', l(2,:)');
                if intsec
                    [x,y] = intsecl(xv, yv, l(1,:)', l(2,:)');
                    f = [f [x;y]];
                end
            end
        end

        function r = difference(p, q)
            %Polygon.difference Difference of polygons
            %
            % D = P.difference(Q) is polygon P minus polygon Q.
            %
            % Notes::
            % - If polygons P and Q are not intersecting, returns
            %   coordinates of P.
            % - If the result D is not simply connected or consists of
            %   several polygons, resulting vertex list will contain NaNs.

            % POLYDIFF  Difference of 2 polygons.
            %       [XO,YO] = POLYDIFF(X1,Y1,X2,Y2) Calculates polygon(s) P
            %   of difference of polygons P1 and P1 with coordinates
            %   X1, Y1 and X2, Y2.
            %   The resulting polygon(s) is a set of all points which belong
            %   to P1 but not to to P2: P = P1 & ~P2.
            %   The input polygons must be non-self-intersecting and
            %   simply connected.
            %
            %       If polygons P1, P2 are not intersecting, returns
            %   coordinates of the first polygon X1, X2.
            %   If the result P is not simply connected or consists of several
            %   polygons, resulting boundary consists of concatenated
            %   coordinates of these polygons, separated by NaN.
            %  Copyright (c) 1995 by Kirill K. Pankratov,
            %       kirill@plume.mit.edu.
            %       06/25/95

            % Call POLYBOOL with flag=3
            [xo,yo,ind] = polybool(p.x, p.y, q.x, q.y, 3);

            r = Polygon([xo(:) yo(:)]');
        end

        function r = intersection(p, q)
            %Polygon.intersection Intersection of polygons
            %
            % I = P.intersection(Q) is a Polygon representing the
            % intersection of polygons P and Q.
            %
            % Notes::
            % - If these polygons are not intersecting, returns empty polygon.
            % - If intersection consist of several disjoint polygons
            %   (for non-convex P or Q) then vertices of I is the concatenation
            %   of the vertices of these polygons.

            % POLYINTS  Intersection of 2 polygons.
            %   [XO,YO] = POLYINTS(X1,Y1,X2,Y2) Calculates polygon(s)
            %   if intersection of polygons with coordinates X1, Y1
            %   and X2, Y2.
            %       The resulting polygon(s) is a set of all points which
            %   belong to both P1 and P2: P = P1 & P2.
            %   These polygons must be non-self-intersecting and
            %   simply connected.
            %
            %   If these polygons are not intersecting, returns empty.
            %   If intersection consist of several disjoint polygons
            %   (for non-convex P1 or P2) output vectors XO, YO consist
            %   of concatenated cooddinates of these polygons,
            %  Copyright (c) 1995 by Kirill K. Pankratov,
            %       kirill@plume.mit.edu.
            %       06/25/95

            % Call POLYBOOL with flag=1
            [xo,yo,ind] = polybool(p.x, p.y, q.x, q.y, 1);

            r = Polygon([xo(:) yo(:)]');
        end

        function r = union(p, q)
            %Polygon.union Union of polygons
            %
            % I = P.union(Q) is a polygon representing the
            % union of polygons P and Q.
            %
            % Notes::
            % - If these polygons are not intersecting, returns a polygon with
            %   vertices of both polygons separated by NaNs.
            % - If the result P is not simply connected (such as a polygon
            %   with a "hole") the resulting contour consist of counter-
            %   clockwise "outer boundary" and one or more clock-wise
            %   "inner boundaries" around "holes".

            % POLYUNI  Union of 2 polygons.
            %       [XO,YO] = POLYINT(X1,Y1,X2,Y2) Calculates polygon(s) P
            %       which is (are) union of polygons P1 and P2 with coordinates
            %   X1, Y1 and X2, Y2.
            %   The resulting polygon(s) is a set of all points which belong
            %   either to P1 or to P2: P = P1 | P2.
            %   The input polygons must be non-self-intersecting and
            %   simply connected.
            %
            %       If polygons P1, P2 are not intersecting, returns
            %   coordinates of the both polygons separated by NaN.
            %   If both P1 and P2 are convex, their boundaries can have no
            %   more than 2 intersections. The result is also a convex
            %   polygon.
            %       If the result P is not simply connected (such as a polygon
            %   with a "hole") the resulting contour consist of counter-
            %   clockwise "outer boundary" and one or more clock-wise
            %   "inner boundaries" around "holes".
            %  Copyright (c) 1995 by Kirill K. Pankratov,
            %       kirill@plume.mit.edu.
            %       06/25/95

            % Call POLYBOOL with flag=2 ..........
            [xo,yo,ind] = polybool(p.x, p.y, q.x, q.y, 2);

            r = Polygon([xo(:) yo(:)]');
        end

        function r = xor(p, q)
            %Polygon.xor Exclusive or of polygons
            %
            % I = P.union(Q) is a polygon representing the
            % exclusive-or of polygons P and Q.
            %
            % Notes::
            % - If these polygons are not intersecting, returns a polygon with
            %   vertices of both polygons separated by NaNs.
            % - If the result P is not simply connected (such as a polygon
            %   with a "hole") the resulting contour consist of counter-
            %   clockwise "outer boundary" and one or more clock-wise
            %   "inner boundaries" around "holes".

            % POLYXOR  Exclusive OR of 2 polygons.
            %       [XO,YO] = POLYXOR(X1,Y1,X2,Y2) Calculates polygon(s) P
            %   of difference of polygons P1 and P1 with coordinates
            %   X1, Y1 and X2, Y2.
            %   The resulting polygon(s) is a set of all points which belong
            %   either to P1 or to P2 but not to both:
            %   P = (P1 & ~P2) | (P2 & ~P1).
            %   The input polygons must be non-self-intersecting and
            %   simply connected.
            %
            %       If polygons P1, P2 are not intersecting, returns
            %   coordinates of the both polygons separated by NaN.
            %   If the result P is not simply connected or consists of several
            %   polygons, resulting boundary consists of concatenated
            %   coordinates of these polygons, separated by NaN.

            %  Copyright (c) 1995 by Kirill K. Pankratov,
            %       kirill@plume.mit.edu.
            %       06/25/95

            % Call POLYBOOL twice with flag=3
            [xx,yy,ind] = polybool(p.x, p.y, q.x, q.y, 3);

            xo = [xx; NaN]; yo = [yy; NaN];
            [xx,yy,ind] = polybool(q.x, q.y, p.x, p.y, 3);

            xo = [xo; xx]; yo = [yo; yy];

            r = Polygon([xo(:) yo(:)]');

        end
    end % methods
end % classdef

function [is,in,un] = interval(x1,x2)

    % Intersection and union of 2 intervals.
    %   [IS,IN,UN] = INTERVAL(X1,X2) calculates pair-wise
    %   intersection IN and union UN of N pairs of
    %   intervals with coordinates X1 and X2 (both are
    %   2 by N vectors). Returns 1 by N boolean vector IS
    %   equal to 1 if intervals have non-empty intersection
    %   and 0 if they don't.

    %  Copyright (c) 1995 by Kirill K. Pankratov,
    %       kirill@plume.mit.edu.
    %       08/24/95

    % Handle input ...........................
    if nargin==0, help interval, return, end
    if nargin==1
        un = x1;
    else
        un = [x1; x2];
    end

    [in,un] = sort(un);     % Sort both intervals together
    un = un(1:2,:)-1;
    is = sum(floor(un/2));  % Check for [0 0 1 1] or [1 1 0 0]
    is = (is==1);
    ii = find(in(2,:)==in(3,:));
    is(ii) = .5*ones(size(ii));

    % Extract intersection and union from sorted coordinates
    if nargout>1
        un = in([1 4],:);
        in = in(2:3,:);
        in(:,~is) = flipud(in(:,~is));
    end
end

function [is,S] = iscross(x1,y1,x2,y2,tol)

    % ISCROSS  Finds whether pairs of lines cross each other
    %   [IS,S] = ISCROSS(X1,Y1,X2,Y2) where arguments X1, Y1,
    %   X2, Y2 are all 2 by N matrices are coordinates of
    %   ends of the pairs of line segments.
    %   Returns vector  IS (1 by N)  consisting of ones if
    %   corresponding pairs cross each other, zeros if they
    %   don't and .5 if an end of one line segment lies on
    %   another segment.
    %   Also returns a matrix  S (4 by N) with each row
    %   consisting of cross products (double areas of
    %   corresponding triangles) built on the following points:
    %   (X2(1,:),Y2(1,:)),(X1(1,:),Y1(1,:)),(X2(2,:),Y2(2,:)),
    %   (X2(1,:),Y2(1,:)),(X1(2,:),Y1(2,:)),(X2(2,:),Y2(2,:))
    %   (X1(1,:),Y1(1,:)),(X2(1,:),Y2(1,:)),(X1(2,:),Y1(2,:))
    %   (X1(1,:),Y1(1,:)),(X2(2,:),Y2(2,:)),(X1(2,:),Y1(2,:))
    %   The signs of these 4 areas can be used to determine
    %   whether these lines and their continuations cross each
    %   other.
    %   [IS,S] = ISCROSS(X1,Y1,X2,Y2,TOL) uses tolerance TOL
    %   for detecting the crossings (default is 0).

    %  Copyright (c) 1995 by Kirill K. Pankratov
    %       kirill@plume.mit.edu
    %       08/14/94, 05/18/95, 08/25/95

    % Defaults and parameters .......................
    tol_dflt = 0; % Tolerance for area calculation
    is_chk = 1;   % Check input arguments

    % Handle input ..................................
    if nargin==0, help iscross, return, end
    if nargin<4           % Check if all 4 entered
        error('  Not enough input arguments')
    end
    if nargin<5, tol = tol_dflt; end
    if tol < 0, is_chk = 0; tol = 0; end

    % Check the format of arguments .................
    if is_chk
        [x1,y1,x2,y2] = linechk(x1,y1,x2,y2);
    end

    len = size(x1,2);
    o2 = ones(2,1);

    % Find if the ranges of pairs of segments intersect
    [isx,S,A] = interval(x1,x2);
    scx = diff(A);
    [isy,S,A] = interval(y1,y2);
    scy = diff(A);
    is = isx & isy;

    % If S values are not needed, extract only those pairs
    % which have intersecting ranges ..............
    if nargout < 2
        isx = find(is);  % Indices of pairs to be checked
        % further
        x1 = x1(:,isx);
        x2 = x2(:,isx);
        y1 = y1(:,isx);
        y2 = y2(:,isx);
        is = is(isx);
        if isempty(is), is = zeros(1,len); return, end
        scx = scx(isx);
        scy = scy(isx);
    end

    % Rescale by ranges ...........................
    x1 = x1.*scx(o2,:);
    x2 = x2.*scx(o2,:);
    y1 = y1.*scy(o2,:);
    y2 = y2.*scy(o2,:);

    % Calculate areas .............................
    S = zeros(4,length(scx));
    S(1,:) = (x2(1,:)-x1(1,:)).*(y2(2,:)-y1(1,:));
    S(1,:) = S(1,:)-(x2(2,:)-x1(1,:)).*(y2(1,:)-y1(1,:));

    S(2,:) = (x2(1,:)-x1(2,:)).*(y2(2,:)-y1(2,:));
    S(2,:) = S(2,:)-(x2(2,:)-x1(2,:)).*(y2(1,:)-y1(2,:));

    S(3,:) = (x1(1,:)-x2(1,:)).*(y1(2,:)-y2(1,:));
    S(3,:) = S(3,:)-(x1(2,:)-x2(1,:)).*(y1(1,:)-y2(1,:));

    S(4,:) = (x1(1,:)-x2(2,:)).*(y1(2,:)-y2(2,:));
    S(4,:) = S(4,:)-(x1(2,:)-x2(2,:)).*(y1(1,:)-y2(2,:));

    % Find if they cross each other ...............
    is = (S(1,:).*S(2,:)<=0)&(S(3,:).*S(4,:)<=0);

    % Find very close to intersection
    isy = min(abs(S));
    ii = find(isy<=tol & is);
    is(ii) = .5*ones(size(ii));

    % Output
    if nargout < 2
        isy = zeros(1,len);
        isy(isx) = is;
        is = isy;

    else
        isy = scx.*scy;
        ii = find(~isy);
        isy(ii) = ones(size(ii));
        S = S./isy(ones(4,1),:);

    end

end

function [xo,yo,ind] = polybool(x1,y1,x2,y2,flag)

    %   [XO,YO] = POLYBOOL(X1,Y1,X2,Y2,FLAG)
    %   calulates results of Boolean operations on
    %   a pair of polygons.
    %   FLAG Specifies the type of the operation:
    %   1 - Intersection (P1 & P2)
    %   2 - Union (P1 | P2)
    %   3 - Difference (P1 & ~P2)

    %  Copyright (c) 1995 by Kirill K. Pankratov,
    %       kirill@plume.mit.edu.
    %       06/25/95, 09/07/95

    %   This program calls the following functions:
    %   AREA, ISINTPL, ISCROSS, INTSECL.

    % Algorithm:
    %  1. Check boundary contour directions (area).
    %     For intersection and union make all
    %     counter-clockwise. For difference make the second
    %    contour clock-wise.
    %  2. Calculate matrix of intersections (function ISINTPL).
    %     Quick exit if no intersections.
    %  3. For intersecting segments calculate intersection
    %     coordinates (function INTSECL).
    %  4. Sort intersections along both contours.
    %  5. Calculate sign of cross-product between intersectiong
    %     segments. This will give which contour goes "in" and
    %     "out" at intersections.
    %
    %  6. Start with first intersection:
    %     Determine direction to go ("in" for intersection,
    %     "out" for union).
    %     Move until next intersection, switch polygons at each
    %     intersection until coming to the initial point.
    %     If not all intersections are encountered, the
    %     resulting polygon is disjoint. Separate output
    %     coordinates by NaN and repeat procedure until all
    %     intersections are counted.

    % Default for flag
    flag_dflt = 1; % 1- intersec., 2-union, 3 - diff.

    % Handle input
    if nargin==0, help polybool, return, end
    if nargin < 4
        error(' Not enough input arguments')
    end
    if nargin<5, flag = flag_dflt; end

    x1 = x1(:); y1 = y1(:);
    x2 = x2(:); y2 = y2(:);
    l1 = length(x1);
    l2 = length(x2);

    % Check areas and reverse if negative
    nn1 = area(x1,y1);
    if nn1<0, x1 = flipud(x1); y1 = flipud(y1); end
    nn2 = area(x2,y2);
    if (nn2<0 & flag<3) | (nn2>0 & flag==3)
        x2 = flipud(x2); y2 = flipud(y2);
    end

    % If both polygons are identical ........
    if l1==l2
        if all(x1==x2) & all(y1==y2)
            if flag<3, xo = x1; yo = y1; ind = 1:l1;
            else, xo = []; yo = []; ind = []; end
            return
        end
    end

    % Calculate matrix of intersections .....
    [is,C] = isintpl(x1,y1,x2,y2);
    is = any(any(C));

    % Quick exit if no intersections ........
    if ~is
        if flag==1       % Intersection
            xo=[]; yo = [];
        elseif flag==2   % Union
            xo = [x1; nan; x2];
            yo = [y1; nan; y2];
        elseif flag==3   % Difference
            xo = x1; yo = y1;
        end
        return
    end

    % Mark intersections with unique numbers
    i1 = find(C);
    ni = length(i1);
    C(i1) = 1:ni;

    % Close polygon contours
    x1 = [x1; x1(1)]; y1 = [y1; y1(1)];
    x2 = [x2; x2(1)]; y2 = [y2; y2(1)];
    l1 = length(x1);  l2 = length(x2);

    % Calculate intersections themselves
    [i1,i2,id] = find(C);
    xs1 = [x1(i1) x1(i1+1)]'; ys1 = [y1(i1) y1(i1+1)]';
    xs2 = [x2(i2) x2(i2+1)]'; ys2 = [y2(i2) y2(i2+1)]';

    % Call INTSECL ............................
    [xint,yint] = intsecl(xs1,ys1,xs2,ys2);

    % For sements belonging to the same line
    % find interval of intersection ...........
    ii = find(xint==inf);
    if ~isempty(ii)
        [is,inx] = interval(xs1(:,ii),xs2(:,ii));
        [is,iny] = interval(ys1(:,ii),ys2(:,ii));
        xint(ii) = mean(inx);
        yint(ii) = mean(iny);
    end

    % Coordinate differences of intersecting segments
    xs1 = diff(xs1); ys1 = diff(ys1);
    xs2 = diff(xs2); ys2 = diff(ys2);

    % Calculate cross-products
    cp = xs1.*ys2-xs2.*ys1;
    cp = cp>0;
    if flag==2, cp=~cp; end % Reverse if union
    cp(ii) = 2*ones(size(ii));

    % Sort intersections along the contours
    ind = (xint-x1(i1)').^2+(yint-y1(i1)').^2;
    ind = ind./(xs1.^2+ys1.^2);
    cnd = min(ind(ind>0));
    ind = ind+i1'+i2'/(ni+1)*cnd*0;
    [xo,ii] = sort(ind);
    xs1 = id(ii);
    [xo,ind] = sort(xs1);
    ind = rem(ind,ni)+1;
    xs1 = xs1(ind);

    ind = (xint-x2(i2)').^2+(yint-y2(i2)').^2;
    ind = ind./(xs2.^2+ys2.^2);
    cnd = min(ind(ind>0));
    [xo,ii] = sort(i2'+ind+i1'/(ni+1)*cnd*0);
    xs2 = id(ii);
    [xo,ind] = sort(xs2);
    ind = rem(ind,ni)+1;
    xs2 = xs2(ind);

    % Combine coordinates in one vector
    x1 = [x1; x2]; y1 = [y1; y2];

    % Find max. possible length of a chain
    xo = find(any(C'));
    xo = diff([xo xo(1)+l1]);
    mlen(1) = max(xo);
    xo = find(any(C));
    xo = diff([xo xo(1)+l2]);
    mlen(2) = max(xo);

    % Check if multiple intersections in one segment
    xo = diff([i1 i2]);
    is_1 = ~all(all(xo));

    % Begin counting intersections *********************

    % Initialization ..................
    int = zeros(size(xint));
    nn = 1;   % First intersection
    nn1 = i1(nn); nn2 = i2(nn);
    b = cp(nn);
    is2 = b==2;
    xo = []; yo = []; ind = [];
    closed = 0;

    % Proceed until all intersections are counted
    while ~closed  % begin counting `````````````````````0

        % If contour closes, find new starting point
        if int(nn) & ~all(int)
            ii = find(int);
            C(id(ii)) = zeros(size(ii));
            nn = min(find(~int));  % Next intersection
            nn1 = i1(nn);
            nn2 = i2(nn);
            xo = [xo; nan];        % Separate by NaN
            yo = [yo; nan];
            ind = [ind; nan];
            % Choose direction ......
            b = cp(nn);
        end

        % Add current intersection ......
        xo = [xo; xint(nn)];
        yo = [yo; yint(nn)];
        ind = [ind; 0];
        int(nn) = 1;
        closed = all(int);

        % Find next segment
        % Indices for next intersection
        if ~b, nn = xs1(nn);
        else,  nn = xs2(nn);
        end
        if ~b, pt0 = nn1; else,  pt0 = nn2; end

        nn1 = i1(nn);
        nn2 = i2(nn);

        if b, pt = nn2; else, pt = nn1; end

        if b, pt0 = pt0+l1; pt = pt+l1; end
        ii = (pt0+1:pt);

        % Go through the beginning ..............
        cnd = pt<pt0 | (pt==pt0 & is_1 & flag>1);
        if cnd
            if ~b,  ii = [pt0+1:l1 1:pt];
            else,   ii = [pt0+1:l1+l2 l1+1:pt];
            end
        end
        len = length(ii);
        cnd = b & len>mlen(2);
        cnd = cnd | (~b & len>mlen(1));
        if is2 | cnd, ii=[]; end

        % Add new segment
        xo = [xo; x1(ii)];
        yo = [yo; y1(ii)];
        ind = [ind; ii'];

        % Switch direction
        if cp(nn)==2, b = ~b; is2 = 1;
        else, b = cp(nn); is2 = 0;
        end

    end    % End while (all intersections) '''''''''''''''0

    % Remove coincident successive points
    ii = find(~diff(xo) & ~diff(yo));
    xo(ii) = []; yo(ii) = []; ind(ii) = [];

    % Remove points which are
    ii = find(isnan(xo));
    if ~isempty(ii)
        i2 = ones(size(xo));
        ii = [ii; length(xo)+1];

        i1 = find(diff(ii)==3);
        i1 = ii(i1);
        i1 = [i1; i1+1; i1+2];
        i2(i1) = zeros(size(i1));

        i1 = find(diff(ii)==2);
        i1 = ii(i1);
        i1 = [i1; i1+1];
        i2(i1) = zeros(size(i1));

        xo = xo(i2); yo = yo(i2); ind = ind(i2);
    end
end

function  [xo,yo] = intsecpl(xv,yv,xl,yl,trace)

    % INTSECPL Intersection of a polygon and a line.
    %   [XI,YI] = INTSECPL(XV,YV,XL,YL) calculates
    %   intersections XI, YI of a polygon with vertices XV,
    %   YV and a line specified by pairs of end coordinates
    %   XL = [XL0 XL1], YL = [YL0 YL1]. Line is assumed to
    %   continue beyond the range of end points.
    %   INTSECPL(XV,YV,[A B]) uses another specification for
    %   a line: Y = A*X+B.
    %
    %   If a line does not intersect polygon, returns empty
    %   XI, YI.
    %   For convex polygon maximum number of intersections is
    %   2, for non-convex polygons multiple intersections are
    %   possible.
    %
    %   INTSECPL(XV,YV,XL,YL)  by itself or
    %   [XI,YI] = INTSECPL(XV,YV,XL,YL,1) plots polygon,
    %   a line segment and intersection segment(s)
    %   (part(s) of the same line inside the polygon).

    %  Copyright (c) 1995 by Kirill K. Pankratov,
    %       kirill@plume.mit.edu.
    %       06/25/95, 08/27/95, 09/27/95

    %  Calls ISCROSS, INTSECL programs.

    % Defaults and parameters .................................
    tol = 1e-14;  % Tolerance
    marg = tol;   % Margins for polygon frame
    is_ab = 0;    % Default A*X+B  mode

    % Handle input ............................................
    if nargin==0, help intsecpl, return, end
    if nargin < 3
        error(' Not enough input arguments')
    end
    if nargin<5, trace = 0; end
    if nargin==4  % Check if 4-th arg is trace
        if max(size(yl))==1, trace = yl;  is_ab = 1; end
    end
    if nargin==3, is_ab = 1; end
    trace = trace | nargin<2;
    if length(xv)~=length(yv)
        error(' Vectors X, Y must have the same size')
    end

    % Auxillary ...........
    xv = [xv(:); xv(1)];
    yv = [yv(:); yv(1)];
    ii = find(abs(diff(xv))<tol & abs(diff(yv))<tol);
    xv(ii) = []; yv(ii) = [];
    nv = length(xv);
    ov = ones(nv-1,1);

    % Polygon frame
    lim = [min(xv)-marg max(xv)+marg min(yv)-marg max(yv)+marg];
    % Estimate for diameter
    d = sqrt((lim(2)-lim(1)).^2+(lim(4)-lim(3)).^2);

    % Form line segment depending on how line is specified
    if is_ab       % A*X+B  mode ...................

        xl = xl(:);
        if length(xl)<2
            error(' Line is specified by at least two parameters')
        end

        a = xl(1); b = xl(2);
        xl = [lim(1)-1 lim(2)+1];
        yl = a*xl+b;

    else          % [X1 X2],[Y1 Y2]  mode ..........

        x0 = (xl(1)+xl(2))/2;
        y0 = (yl(1)+yl(2))/2;
        dx = xl(2)-x0;
        dy = yl(2)-y0;
        dl = max(abs([lim(1:2)-x0 lim(3:4)-y0]));
        d = max(d,dl);
        dl = sqrt(dx.^2+dy.^2);
        dl = max(d,d/dl);
        dx = dx*dl; dy = dy*dl;
        xl = [x0-dx x0+dx];
        yl = [y0-dy y0+dy];

    end

    % Find intersecting segments ..............................
    is = iscross([xv(1:nv-1) xv(2:nv)]',[yv(1:nv-1) yv(2:nv)]',...
        xl(ov,:)',yl(ov,:)',0);

    % Quick exit if no intersections .........................
    if ~any(is)
        if trace
            % Intersection with polygon frame
            [xl,yl] = intsecpl(lim([1 2 2 1]),lim([3 3 4 4]),xl,yl);
            %plot(xv,yv,xl,yl) % Plotting itself
        end
        xo = []; yo = [];
        return
    end

    % For segments touching the line (is==.5) find whether pairs of
    % successive segments are on the same side
    ii = find(is==.5)';
    if ii~=[]
        xo = [ii-1 ii+1];
        xo = xo+(nv-1)*(xo<1);
        yo = iscross([xv(xo(:,1)) xv(xo(:,2))]',[yv(xo(:,1)) yv(xo(:,2))]',...
            xl,yl,tol);
        ii = ii(find(yo==1));
        is(ii) = zeros(size(ii));
    end

    % Calculate intersection coordinates ......................
    ii = find(is);
    oi = ones(size(ii));
    [xo,yo] = intsecl([xv(ii) xv(ii+1)]',[yv(ii) yv(ii+1)]',...
        xl(oi,:)',yl(oi,:)');

    dx = find(~finite(xo));
    xo(dx) = []; yo(dx) = [];
    ii(dx) = []; oi(dx) = [];

    % Sort intersections along the line ..........
    xo = xo(:); yo = yo(:);
    if any(diff(xo))
        [xo,ii] = sort(xo);
        yo = yo(ii);

    else

        [yo,ii] = sort(yo);
        xo = xo(ii);
    end

    % Exclude repeated points (degenerate cases)
    % ///////// It seems that this is not needed, figure this
    % later ///////////////////
    if length(ii)>1 & 0  % Do not execute
        xx = [xo yo];
        yy = diff(xx)';
        ii = [1 find(any(abs(yy)>tol))+1];
        xo = xx(ii,1); yo = xx(ii,2);
        oi = ones(size(xo));
    end

    % Plotting ................................................
    if trace
        oi(3:2:length(oi)) = oi(3:2:length(oi))+1;
        oi = cumsum(oi);
        len = max(oi);
        xp = nan*ones(len,1); yp = xp;
        xp(oi) = xo;
        yp(oi) = yo;

        % Intersection with polygon frame
        [xl,yl] = intsecpl(lim([1 2 2 1]),lim([3 3 4 4]),xl,yl);

        plot(xv,yv,xl,yl,xp,yp) % Plotting itself
    end
end

function [is,S] = isintpl(x1,y1,x2,y2)

    % ISINTPL Check for intersection of polygons.
    %   [IS,S] = ISINTPL(X1,Y1,X2,Y2) Accepts coordinates
    %   X1,Y1 and X2, Y2 of two polygons. Returns scalar
    %   IS equal to 1 if these polygons intersect each other
    %   and 0 if they do not.
    %   Also returns Boolean matrix S of the size length(X1)
    %   by length(X2) so that {ij} element of which is 1 if
    %   line segments i to i+1 of the first polygon and
    %   j to j+1 of the second polygon intersect each other,
    %   0 if they don't and .5 if they "touch" each other.

    %  Copyright (c) 1995 by Kirill K. Pankratov,
    %       kirill@plume.mit.edu.
    %       06/20/95, 08/25/95

    % Handle input ...................................
    if nargin==0, help isintpl, return, end
    if nargin<4
        error(' Not enough input arguments ')
    end

    % Make column vectors and check sizes ............
    x1 = x1(:); y1 = y1(:);
    x2 = x2(:); y2 = y2(:);
    l1 = length(x1);
    l2 = length(x2);
    if length(y1)~=l1 | length(y2)~=l2
        error('(X1,Y1) and (X2,Y2) must pair-wise have the same length.')
    end

    % Quick exit if empty
    if l1<1 | l2<1, is = []; S = []; return, end

    % Check if their ranges are intersecting .........
    lim1 = [min(x1) max(x1)]';
    lim2 = [min(x2) max(x2)]';
    isx = interval(lim1,lim2);  % X-ranges
    lim1 = [min(y1) max(y1)]';
    lim2 = [min(y2) max(y2)]';
    isy = interval(lim1,lim2);  % Y-ranges
    is = isx & isy;
    S = zeros(l2,l1);

    if ~is, return, end  % Early exit if disparate limits

    % Indexing .......................................
    [i11,i12] = meshgrid(1:l1,1:l2);
    [i21,i22] = meshgrid([2:l1 1],[2:l2 1]);
    i11 = i11(:); i12 = i12(:);
    i21 = i21(:); i22 = i22(:);

    % Calculate matrix of intersections ..............
    S(:) = iscross([x1(i11) x1(i21)]',[y1(i11) y1(i21)]',...
        [x2(i12) x2(i22)]',[y2(i12) y2(i22)]')';

    S = S';
    is  = any(any(S));
end

function [xo,yo] = intsecl(x1,y1,x2,y2,tol)

    % INTSECL Intersection coordinates of two line segments.
    %       [XI,YI] = INTSECL(X1,Y1,X2,Y2) where all 4
    %   arguments are 2 by N matrices with coordinates
    %   of ends of N pairs of line segments (so that
    %       the command such as PLOT(X1,Y1,X2,Y2) will plot
    %       these pairs of lines).
    %       Returns 1 by N vectors XI and YI consisting of
    %       coordinates of intersection points of each of N
    %   pairs of lines.
    %
    %   Special cases:
    %   When a line segment is degenerate into a point
    %   and does not lie on line through the other segment
    %   of a pair returns XI=NaN while YI has the following
    %   values: 1 - when the first segment in a pair is
    %   degenerate, 2 - second segment, 0 - both segments
    %   are degenerate.
    %   When a pair of line segments is parallel, returns
    %   XI = Inf while YI is 1 for coincident lines,
    %   0 - for parallel non-coincident ones.
    %   INTSECL(X1,Y1,X2,Y2,TOL) also specifies tolerance
    %   in detecting coincident points in different line
    %   segments.

    %  Copyright (c) 1995 by Kirill K. Pankratov
    %       kirill@plume.mit.edu
    %       04/15/94, 08/14/94, 05/10/95, 08/23/95

    % Defaults and parameters .........................
    tol_dflt = 0; % Tolerance for coincident points
    is_chk = 1;   % Check input arguments

    % Handle input ....................................
    if nargin==0, help intsecl, return, end
    if nargin<4           % Check if all 4 entered
        error('  Not enough input arguments')
    end
    if nargin<5, tol = tol_dflt; end
    if tol < 0, is_chk = 0; tol = 0; end

    % Check the format of arguments .......
    if is_chk
        [x1,y1,x2,y2] = linechk(x1,y1,x2,y2);
    end

    % Auxillary
    o2 = ones(2,1);
    i_pt1 = []; i_pt2 = []; i_pt12 = [];

    % Make first points origins ...........
    xo = x1(1,:);
    yo = y1(1,:);
    x2 = x2-xo(o2,:);
    y2 = y2-yo(o2,:);

    % Differences of first segments .......
    a = x1(2,:)-x1(1,:);
    b = y1(2,:)-y1(1,:);
    s = sqrt(a.^2+b.^2);  % Lengths of first segments
    i_pt1 = find(~s);
    s(i_pt1) = ones(size(i_pt1));
    rr = rand(size(i_pt1));
    a(i_pt1) = cos(rr);
    b(i_pt1) = sin(rr);

    % Normalize by length .................
    a = a./s; b = b./s;

    % Rotate coordinates of the second segment
    tmp = x2.*a(o2,:)+y2.*b(o2,:);
    y2 = -x2.*b(o2,:)+y2.*a(o2,:);
    x2 = tmp;

    % Calculate differences in second segments
    s = x2(2,:)-x2(1,:);
    tmp = y2(2,:)-y2(1,:);
    cc = tmp(i_pt1);

    % Find some degenerate cases .......................

    % Find zeros in differences
    izy2 = find(~tmp);
    tmp(izy2) = ones(size(izy2));

    % Find degenerate and parallel segments
    bool = ~s(izy2);
    i_par = izy2(~bool);
    i_pt2 = izy2(bool);

    bool = abs(y2(1,i_pt2))<=tol;
    i_pt2_off = i_pt2(~bool);
    i_pt2_on = i_pt2(bool);

    if ~isempty(i_par)
        bool = abs(y2(1,i_par))<=tol;
        i_par_off = i_par(~bool);
        i_par_on = i_par(bool);
    end

    % Calculate intercept with rotated x-axis ..........
    tmp = s./tmp;   % Slope
    tmp = x2(1,:)-y2(1,:).*tmp;

    % Rotate and translate back to original coordinates
    xo = tmp.*a+xo;
    yo = tmp.*b+yo;

    % Mark special cases ...................................
    % First segments are degenerate to points
    if ~isempty(i_pt1)
        bool = ~s(i_pt1) & ~cc;
        i_pt12 = i_pt1(bool);
        i_pt1 = i_pt1(~bool);

        bool = abs(tmp(i_pt1))<=tol;
        i_pt1_on = i_pt1(bool);
        i_pt1_off = i_pt1(~bool);

        xo(i_pt1_on) = x1(1,i_pt1_on);
        yo(i_pt1_on) = y1(1,i_pt1_on);

        oo = ones(size(i_pt1_off));
        xo(i_pt1_off) = nan*oo;
        yo(i_pt1_off) = oo;
    end

    % Second segments are degenerate to points ...
    if ~isempty(i_pt2)
        oo = ones(size(i_pt2_off));
        xo(i_pt2_off) = nan*oo;
        yo(i_pt2_off) = 2*oo;
    end

    % Both segments are degenerate ...............
    if ~isempty(i_pt12)
        bool = x1(i_pt12)==xo(i_pt12);
        i_pt12_on = i_pt12(bool);
        i_pt12_off = i_pt12(~bool);

        xo(i_pt12_on) = x1(1,i_pt12_on);
        yo(i_pt12_on) = y1(1,i_pt12_on);

        oo = ones(size(i_pt12_off));
        xo(i_pt12_off) = nan*oo;
        yo(i_pt12_off) = 0*oo;
    end

    % Parallel segments .........................
    if ~isempty(i_par)
        oo = ones(size(i_par_on));
        xo(i_par_on) = inf*oo;
        yo(i_par_on) = oo;

        oo = ones(size(i_par_off));
        xo(i_par_off) = inf*oo;
        yo(i_par_off) = 0*oo;
    end

end

function [x1,y1,x2,y2] = linechk(x1,y1,x2,y2)

    % LINECHK Input checking for line segments.

    %  Copyright (c) 1995 by Kirill K. Pankratov
    %       kirill@plume.mit.edu
    %       08/22/95,

    % String for transposing
    str = ['x1=x1'';'; 'y1=y1'';'; 'x2=x2'';'; 'y2=y2'';'];

    % Sizes
    sz = [size(x1); size(y1); size(x2); size(y2)]';
    psz = prod(sz);

    % Check x1, y1
    if psz(1)~=psz(2)
        error('  Arguments  x1 and y1 must have the same size')
    end

    % Check x2, y2
    if psz(3)~=psz(3)
        error('  Arguments  x2 and y2 must have the same size')
    end

    % Check if any arguments are less than 2 by 1
    if any(max(sz)<2)
        error('  Arguments  x1, y1, x2, y2 must be at least 2 by 1 vectors')
    end

    % Check if no size is equal to 2
    if any(all(sz~=2))
        error('  Arguments  x1, y1, x2, y2 must be 2 by 1 vectors')
    end

    % Find aruments to be transposed .............................
    ii = find(sz(1,:)~=2);
    for jj = 1:length(ii)
        eval(str(ii(jj),:)); % Transpose if neccessary
    end
    sz(:,ii) = flipud(sz(:,ii));

    % If vectors, extend to 2 by n matrices ......................
    n = max(sz(2,:));
    on = ones(1,n);
    if sz(2,1)<n, x1 = x1(:,on); end
    if sz(2,2)<n, y1 = y1(:,on); end
    if sz(2,3)<n, x2 = x2(:,on); end
    if sz(2,4)<n, y2 = y2(:,on); end
end


function partitions = cosmo_nchoosek_partitioner(chunks_or_ds, k, varargin)
% partitions for into nchoosek(n,k) parititions with optional grouping schemas.
%
% partitions=cosmo_nchoosek_partitioner(chunks, k, group_values1, test_group_by1,...)
%
% Input
%  - chunks          Px1 chunk indices for P samples. It can also be a
%                    dataset struct with field .sa.chunks
%  - k               When an integer, k chunks are in each test partition.
%                    When between 0 and 1, this is interpreted as
%                    round(k*nchunks) where nchunks is the number of unique
%                    chunks in chunks.
%                    A special case, mostly aimed at split-half
%                    correlations, is when k=='half'; this sets k to .5,
%                    and if k is even, it treats train_indices and
%                    test_indices as symmetrical, meaning it returns only
%                    half the number of partitions (avoiding duplicates).
%                    If k is odd then train and test indices have
%                    (k+1)/nchunks and (k-1)/nchunks elements,
%                    respectively, or vice versa.
%  - group_values*   } Intended for cross-participant or cross-condition
%  - test_group_by*  } generalizability analyses.
%                      Pairs of these determine a subsequent level group
%                      partition scheme. Each group_values can be
%                      a cell with the labels for a test group, or,
%                      if chunks is a string, the name of a sample
%                      attribute whose values are used as labels.
%                      Each test_group_by indicates which values in
%                      group_values are used as a test value in the
%                      cross-validation scheme (and all other ones are used
%                      as training value). If empty it is set to the unique
%                      values in group_values.
%
% Output:
%  - partitions      A struct with fields:
%     .train_indices } Each of these is an 1xQ cell for Q partitions, where
%     .test_indices  } Q=nchoosek(nchunks,k)). It contains all possible
%                      combinations with k test indices and (nchunks-k)
%                      training indices (except when k=='half', see above).
%                    If group_values* and test_group_by* are specified,
%                    then the number of output partitions is multiplied
%                    by the product of the number of values in
%                    test_group_by*.
%
%
% Examples:
%     % make a simple dataset with 4 chunks, 2 samples each
%     % assume two targets (i.e. conditions, say piano versus guitar)
%     ds=struct();
%     ds.samples=randn(8,99); % 8 samples, 99
%     ds.sa.targets=[1 1 1 1 2 2 2 2]';
%     ds.sa.chunks=2+[1 2 3 4 4 3 2 1]';
%     cosmo_check_dataset(ds); % sanity check
%     %
%     % take-one-chunk out partitioning
%     p=cosmo_nchoosek_partitioner(ds,1);
%     cosmo_disp(p);
%     %|| .train_indices
%     %||   { [ 2    [ 1    [ 1    [ 1
%     %||       3      3      2      2
%     %||       4      4      4      3
%     %||       5      5      5      6
%     %||       6      6      7      7
%     %||       7 ]    8 ]    8 ]    8 ] }
%     %|| .test_indices
%     %||   { [ 1    [ 2    [ 3    [ 4
%     %||       8 ]    7 ]    6 ]    5 ] }
%     %
%     % take-two chunks out partitioning
%     p=cosmo_nchoosek_partitioner(ds,2);
%     cosmo_disp(p);
%     %|| .train_indices
%     %||   { [ 3    [ 2    [ 2    [ 1    [ 1    [ 1
%     %||       4      4      3      4      3      2
%     %||       5      5      6      5      6      7
%     %||       6 ]    7 ]    7 ]    8 ]    8 ]    8 ] }
%     %|| .test_indices
%     %||   { [ 1    [ 1    [ 1    [ 2    [ 2    [ 3
%     %||       2      3      4      3      4      4
%     %||       7      6      5      6      5      5
%     %||       8 ]    8 ]    8 ]    7 ]    7 ]    6 ] }
%     %
%     % take-half-of-the-chunks out partitioning
%     % (this effectively gives same chunks as above)
%     p_alt=cosmo_nchoosek_partitioner(ds,.5);
%     isequal(p, p_alt)
%     %|| true
%     %
%     % do half split (for correlation measure); this leaves out
%     % mirror partitions of train and test indices
%     p=cosmo_nchoosek_partitioner(ds,'half');
%     cosmo_disp(p);
%     %|| .train_indices
%     %||   { [ 1    [ 1    [ 1
%     %||       2      3      4
%     %||       7      6      5
%     %||       8 ]    8 ]    8 ] }
%     %|| .test_indices
%     %||   { [ 3    [ 2    [ 2
%     %||       4      4      3
%     %||       5      5      6
%     %||       6 ]    7 ]    7 ] }
%     %
%     % test on samples with chunk=3 only using take-one-fold out
%     p=cosmo_nchoosek_partitioner(ds,1,'chunks',3);
%     cosmo_disp(p);
%     %|| .train_indices
%     %||   { [ 2
%     %||       3
%     %||       4
%     %||       5
%     %||       6
%     %||       7 ] }
%     %|| .test_indices
%     %||   { [ 1
%     %||       8 ] }
%     % test on samples with chunk=[3 4] only using take-one-fold out;
%     % only samples with chunks=1 or 2 are used for training
%     p=cosmo_nchoosek_partitioner(ds,1,'chunks',{[3 4]});
%     cosmo_disp(p);
%     %|| .train_indices
%     %||   { [ 3    [ 3
%     %||       4      4
%     %||       5      5
%     %||       6 ]    6 ] }
%     %|| .test_indices
%     %||   { [ 1    [ 2
%     %||       8 ]    7 ] }
%     % test separately on samples with chunk=3 and samples with chunk=4;
%     % in some folds, samples with chunks=1,2,4 are used for training, in
%     % other folds samples with chunks=1,2,3 are used for training
%     p=cosmo_nchoosek_partitioner(ds,1,'chunks',{[3],[4]});
%     cosmo_disp(p);
%     %|| .train_indices
%     %||   { [ 2    [ 1
%     %||       3      3
%     %||       4      4
%     %||       5      5
%     %||       6      6
%     %||       7 ]    8 ] }
%     %|| .test_indices
%     %||   { [ 1    [ 2
%     %||       8 ]    7 ] }
%     %||
%
%
%     % make a slightly more complicated dataset: with three chunks,
%     % suppose there are two modalities (e.g. (1) visual and (2)
%     % auditory stimulation) which are stored in an
%     % additional field 'modality'
%     ds=struct();
%     ds.samples=randn(12,99);
%     ds.sa.chunks  =[1 1 1 1 2 2 2 2 3 3 3 3]';
%     ds.sa.targets =[1 2 1 2 1 2 1 2 1 2 1 2]';
%     ds.sa.modality=[1 1 2 2 1 1 2 2 1 1 2 2]';
%     cosmo_check_dataset(ds);
%     %
%     % take-one-chunk out, test on samples with modality=1 (and train
%     % on samples with other modalities, i.e. modality=2)
%     p=cosmo_nchoosek_partitioner(ds,1,'modality',1);
%     cosmo_disp(p);
%     %|| .train_indices
%     %||   { [  7    [  3    [ 3
%     %||        8       4      4
%     %||       11      11      7
%     %||       12 ]    12 ]    8 ] }
%     %|| .test_indices
%     %||   { [ 1    [ 5    [  9
%     %||       2 ]    6 ]    10 ] }
%     %
%     % take-one-chunk out, test on samples with modality=2
%     p=cosmo_nchoosek_partitioner(ds,1,'modality',2);
%     cosmo_disp(p);
%     %|| .train_indices
%     %||   { [  5    [  1    [ 1
%     %||        6       2      2
%     %||        9       9      5
%     %||       10 ]    10 ]    6 ] }
%     %|| .test_indices
%     %||   { [ 3    [ 7    [ 11
%     %||       4 ]    8 ]    12 ] }
%     % take-one-chunk out, test on samples with modality=1 (and train on
%     % modality=2) and vice verse
%     p=cosmo_nchoosek_partitioner(ds,1,'modality',[]);
%     cosmo_disp(p);
%     %|| .train_indices
%     %||   { [  7    [  3    [ 3    [  5    [  1    [ 1
%     %||        8       4      4       6       2      2
%     %||       11      11      7       9       9      5
%     %||       12 ]    12 ]    8 ]    10 ]    10 ]    6 ] }
%     %|| .test_indices
%     %||   { [ 1    [ 5    [  9    [ 3    [ 7    [ 11
%     %||       2 ]    6 ]    10 ]    4 ]    8 ]    12 ] }
%
%     % between-subject classification: 3 chunks, 2 modalities, 5 subjects
%     ds=struct();
%     ds.samples=randn(60,99);
%     ds.sa.targets=repmat([1 2],1,30)';
%     ds.sa.chunks=repmat([1 1 1 1 2 2 2 2 3 3 3 3],1,5)';
%     ds.sa.modality=repmat([1 1 2 2],1,15)';
%     ds.sa.subject=kron(1:5,ones(1,12))';
%     cosmo_check_dataset(ds);
%     %
%     % test on subject 1, train on other subjects using take-one-chunk out
%     p=cosmo_nchoosek_partitioner(ds,1,'subject',1);
%     cosmo_disp(p);
%     %|| .train_indices
%     %||   { [ 17         [ 13         [ 13
%     %||       18           14           14
%     %||       19           15           15
%     %||        :            :            :
%     %||       58           58           54
%     %||       59           59           55
%     %||       60 ]@32x1    60 ]@32x1    56 ]@32x1 }
%     %|| .test_indices
%     %||   { [ 1    [ 5    [  9
%     %||       2      6      10
%     %||       3      7      11
%     %||       4 ]    8 ]    12 ] }
%     %
%     % test on each subject after training on each other subject
%     % in each fold, the test data is from one subject and one chunk,
%     % and the train data from all other subjects and all other chunks.
%     % since there are 5 subjects and 3 chunks, there are 15 folds.
%     p=cosmo_nchoosek_partitioner(ds,1,'subject',[]);
%     cosmo_disp(p);
%     %|| .train_indices
%     %||   { [ 17         [ 13         [ 13        ... [  5         [  1         [  1
%     %||       18           14           14               6            2            2
%     %||       19           15           15               7            3            3
%     %||        :            :            :               :            :            :
%     %||       58           58           54              46           46           42
%     %||       59           59           55              47           47           43
%     %||       60 ]@32x1    60 ]@32x1    56 ]@32x1       48 ]@32x1    48 ]@32x1    44 ]@32x1   }@1x15
%     %|| .test_indices
%     %||   { [ 1    [ 5    [  9   ... [ 49    [ 53    [ 57
%     %||       2      6      10         50      54      58
%     %||       3      7      11         51      55      59
%     %||       4 ]    8 ]    12 ]       52 ]    56 ]    60 ]   }@1x15
%     %
%     % as above, but test on modality=2 (and train on other values for
%     % modality, i.e. modality=1)
%     p=cosmo_nchoosek_partitioner(ds,1,'subject',[],'modality',2);
%     cosmo_disp(p);
%     %|| .train_indices
%     %||   { [ 17         [ 13         [ 13        ... [  5         [  1         [  1
%     %||       18           14           14               6            2            2
%     %||       21           21           17               9            9            5
%     %||        :            :            :               :            :            :
%     %||       54           50           50              42           38           38
%     %||       57           57           53              45           45           41
%     %||       58 ]@16x1    58 ]@16x1    54 ]@16x1       46 ]@16x1    46 ]@16x1    42 ]@16x1   }@1x15
%     %|| .test_indices
%     %||   { [ 3    [ 7    [ 11   ... [ 51    [ 55    [ 59
%     %||       4 ]    8 ]    12 ]       52 ]    56 ]    60 ]   }@1x15
%     %
%     % as above, but test on each modality after training on the other
%     % modality. There are 30 folds (5 subjects, 3 chunks, 2 modalities).
%     p=cosmo_nchoosek_partitioner(ds,1,'subject',[],'modality',[]);
%     cosmo_disp(p);
%     %|| .train_indices
%     %||   { [ 19         [ 15         [ 15        ... [  5         [  1         [  1
%     %||       20           16           16               6            2            2
%     %||       23           23           19               9            9            5
%     %||        :            :            :               :            :            :
%     %||       56           52           52              42           38           38
%     %||       59           59           55              45           45           41
%     %||       60 ]@16x1    60 ]@16x1    56 ]@16x1       46 ]@16x1    46 ]@16x1    42 ]@16x1   }@1x30
%     %|| .test_indices
%     %||   { [ 1    [ 5    [  9   ... [ 51    [ 55    [ 59
%     %||       2 ]    6 ]    10 ]       52 ]    56 ]    60 ]   }@1x30
%
% Notes:
%    - When k=1 and two input arguments, this function behaves equivalently
%      to cosmo_nfold_partitioner. Thus, in the most simple case
%      (nfold-partitioning), cosmo_nfold_partitioner with k=1 can be used
%      as well as this function.
%    - This function does not consider any .sa.targets (trial condition)
%      or .samples information.
%    - As shown in the examples below, this function can be used for
%      cross-modal and/or cross-participant cross-validation.
%    - For cross-validation it is recommended to balance partitions using
%      cosmo_balance_partitions.
%   - this function can be used for cross-decoding analyses. Doing so may
%     require a re-assignment of .sa.targets, and adding another sample
%     attribute to specify which samples are used for training and testing.
%     For example, consider the following dataset with six unique
%     conditions as specified in the sample attribute field .sa:
%
%       .targets    .chunks     .labels
%       1           1           'vis_dog'
%       2           1           'vis_cat'
%       3           1           'vis_frog'
%       4           1           'aud_dog'
%       5           1           'aud_cat'
%       6           1           'aud_frog'
%       1           2           'vis_dog'
%       2           2           'vis_cat'
%       :           :               :
%       6           8           'aud_frog'
%
%    This dataset has 8 chunks, each with 6 conditions: three for visual
%    stimuli of dogs, cats, and frogs, and three for auditory stimuli for
%    the same animals. The field .labels is not required, but used for a
%    human-readable description of the condition of each sample.
%
%    Suppose that one wants to do cross-decoding to see
%    if discrimination of animals generalizes between the visual and
%    auditory modalities.
%    To do so, the user has to:
%       * change the .targets field, to indicate the anima species,
%       * add another field (here 'modality') indicating which samples are
%         used for the cross-decoding
%
%    In this example, the sample attribute field .sa can be set as follows:
%
%       .targets    .chunks     .labels     .modality
%       1           1           'vis_dog'   1
%       2           1           'vis_cat'   1
%       3           1           'vis_frog'  1
%       1           1           'aud_dog'   2
%       2           1           'aud_cat'   2
%       3           1           'aud_frog'  2
%       1           2           'vis_dog'   1
%       2           2           'vis_cat'   1
%       :           :               :
%       6           8           'aud_frog'  2
%
%    so that:
%     * .modality corresponds to the visual (=1) and auditory (=2)
%       modality.
%     * .targets corresponds to the dog (=1), cat (=2), and frog (=3)
%       species.
%
%    With this re-assignment of .targets, testing on the auditory modality
%    and training on the visual modality with take-one-chunk out
%    cross-validation can be done using:
%
%       test_modality=2; % train on all other modalities (here: only 1)
%       partitions=cosmo_nchoosek_partitioner(ds,1,...
%                                   'modality',test_modality);
%
%    If test_modality is set to empty ([]), then both modalities are used
%    for training and for testing (in separate folds).
%
% See also: cosmo_nfold_partitioner, cosmo_balance_partitions
%
% #   For CoSMoMVPA's copyright information and license terms,   #
% #   see the COPYING file distributed with CoSMoMVPA.           #

    % if the first input is a dataset, get the chunks from it
    is_ds=isstruct(chunks_or_ds);
    if is_ds
        if isfield(chunks_or_ds,'sa') && isfield(chunks_or_ds.sa,'chunks')
            % it is a dataset
            ds=chunks_or_ds;
            chunks=ds.sa.chunks;
        else
            error('illegal struct input');
        end
    else
        chunks=chunks_or_ds;
    end

    % use helper function defined below
    partitions=nchoosek_partitioner(chunks,k);

    if nargin<3
        return;
    elseif mod(nargin,2)~=0
        error('Need even number of arguments');
    else
        nsamples=numel(chunks);
        for k=1:2:(nargin-2);
            % loop over grouping values
            group_values=varargin{k};
            test_group_by=varargin{k+1};

            % get sample attribute, if input was dataset
            if ischar(group_values)
                if is_ds
                    group_values=ds.sa.(group_values);
                else
                    error('String for group value requires dataset input');
                end
            end

            % check number of values matches
            ngroup_by=numel(group_values);
            if ngroup_by ~= nsamples
                error('group_by has %d values, expected %d', ...
                                ngroup_by, nsamples);
            end

            if isempty(test_group_by)
                test_group_by=num2cell(unique(group_values));
            elseif isnumeric(test_group_by)
                test_group_by={test_group_by};
            end

            % update partitions using helper function below
            partitions=group_by(partitions, group_values, test_group_by);
        end
    end

function partitions=group_by(partitions, group_values, test_group_by)
    % helper function to group partitions
    % the output has N times as many partitions as the input,
    % where N=numel(test_by).

    npartitions=numel(partitions.test_indices);

    assert(iscell(test_group_by));
    % see which values to split on
    ntest_group_by=numel(test_group_by);

    % allocate space for output
    train_indices=cell(1,ntest_group_by);
    test_indices=cell(1,ntest_group_by);

    % run for each unique value in group_by_values
    for m=1:ntest_group_by
        test_by=test_group_by{m};

        % allocate space for this iteration
        train_indices_cell=cell(1,npartitions);
        test_indices_cell=cell(1,npartitions);

        % some filtered partitions may be empty, so keep track of the
        % last position where a value was stored
        pos=0;
        for j=1:npartitions
            % get testing chunk indices
            p_test=partitions.test_indices{j};

            % see which ones match the group_by_value
            msk_test=cosmo_match(group_values(p_test),test_by);

            % keep just those indices
            p_test_masked=p_test(msk_test);

            % the same for training, but different from test_by
            p_train=partitions.train_indices{j};
            msk_train=~cosmo_match(group_values(p_train),test_by);
            p_train_masked=p_train(msk_train);

            if ~isempty(p_test_masked) && ~isempty(p_train_masked)
                % both training and test set are non-empty, so keep result
                pos=pos+1;
                test_indices_cell{pos}=p_test_masked;
                train_indices_cell{pos}=p_train_masked;
            end
        end

        % store the test-indices for m-th group_by_value
        test_indices{m}=test_indices_cell(1:pos);
        train_indices{m}=train_indices_cell(1:pos);
    end

    partitions=struct();

    % concatenate results for training and test indices
    partitions.train_indices=cat(2,train_indices{:});
    partitions.test_indices=cat(2,test_indices{:});

function partitions=nchoosek_partitioner(chunks,k)
% straightfoward partitioner

[unq,unused,chunk_indices]=unique(chunks);
nchunks=numel(unq);

if nchunks<2
    error(['at least two unique values for .sa.chunks are required, '...
                    'found %d.'], nchunks);
end

% deal with special 'half' case
is_symmetric=false;
if ischar(k)
    if strcmp(k,'half')
        k=.5;
        is_symmetric=true;
    else
        error('illegal string k');
    end
end
if isnumeric(k)
    if ~isscalar(k)
        error('k must be a scalar');
    end

    if k ~= round(k)
        k=round(nchunks*k);
    end

    if ~any(k==1:(nchunks-1))
        error('illegal k=%d: test class count should be between 1 and %d', ...
                    k, nchunks-1);
    end
else
    error('illegal parameter for k');
end

% little optimization: if just two chunks, the split is easy
if all(cosmo_match(chunks,[1 2]))
    chunk_msk1=chunks==1;
    chunk1_idxs=find(chunk_msk1);
    chunk2_idxs=find(~chunk_msk1);

    partitions=struct();
    if is_symmetric
        partitions.train_indices={chunk1_idxs};
        partitions.test_indices={chunk2_idxs};
    else
        partitions.train_indices={chunk1_idxs,chunk2_idxs};
        partitions.test_indices={chunk2_idxs,chunk1_idxs};
    end
    return
end

npartitions=nchoosek(nchunks,k);
combis=nchoosek(1:nchunks,k); % make all combinations

if is_symmetric && mod(nchunks,2)==0
    % when nclasses is even, return the first half of the permutations:
    % the current implementation of nchoosek results is that
    % combis(k,:) and combis(npartitions+1-k) are complementary
    % (i.e. together they make up 1:nchunks). In principle this could
    % change in the future leading to wrong results (if Mathworks, in its
    % infinite wisdom, decides to change the implementation of nchoosek),
    % so to be sure we check that the output matches what is expected.
    % The rationale is that this reduces computation time of subsequent
    % analyses by half, in particular for commonly used split half
    % correlations.
    nhalf=npartitions/2;

    check_combis=[combis(1:nhalf,:) combis(npartitions:-1:(nhalf+1),:)];

    % each row, when sorted, should be 1:nchunks
    matches=bsxfun(@eq,sort(check_combis,2),1:nchunks);
    assert(all(matches(:)),'nchoosek behaves weirdly');

    % we're good - just take the second half and update npartitions
    combis=combis(npartitions:-1:(nhalf+1),:);
    npartitions=nhalf;
end

% allocate space for output
train_indices=cell(1,npartitions);
test_indices=cell(1,npartitions);

% make all partitions
for j=1:npartitions
    combi=combis(j,:);
    sample_count=cosmo_match(chunk_indices,combi);
    test_indices{j}=find(sample_count);
    train_indices{j}=find(~sample_count);
end

partitions=struct();
partitions.train_indices=train_indices;
partitions.test_indices=test_indices;


%%*******************************************************************
%%  Read in a problem in SDPA sparse format.
%%
%%  [blk,At,C,b] = read_sdpa(fname)
%%
%%  Input: fname = name of the file containing SDP data in
%%                 SDPA foramt.
%%  Important: the data is assumed to contain only
%%             semidefinite and linear blocks.
%%
%% SDPT3: version 3.1
%% Copyright (c) 1997 by
%% K.C. Toh, M.J. Todd, R.H. Tutuncu
%% Last Modified: 16 Sep 2004
%%******************************************************************

   function [blk,At,C,b] = read_sdpa(fname);

%%
%%  Open the file for input
%%
   compressed = 0;
   if exist(fname)
      fid = fopen(fname,'r');
   elseif exist([fname,'.Z']);
      compressed = 1;
      unix(['uncompress ',fname,'.Z']);
      fid = fopen(fname,'r');
   elseif exist([fname,'.gz']);
      compressed = 2;
      unix(['gunzip ',fname,'.gz']);
      fid = fopen(fname,'r');
   else
      fprintf('*** Problem not found, please specify the correct path or problem name. \n');
      blk = []; At = []; C = []; b = [];
      return;
   end
%%
%%  Clean up special characters and comments from the file
%%
   [datavec,count] = fscanf(fid,'%c');
   linefeeds = findstr(datavec,char(10));
   comment_chars = '*"=';
   cumidx = [];
   for i=1:length(comment_chars)
      idx = findstr(datavec,comment_chars(i));
      cumidx = [cumidx,idx];
   end
   for j=length(cumidx):-1:1
      if (cumidx(j)==1) | (strcmp(datavec(cumidx(j)-1),char(10)))
         datavec(cumidx(j):linefeeds(min(find(cumidx(j)<linefeeds))))='';
      else
         datavec(cumidx(j):linefeeds(min(find(cumidx(j)<linefeeds)))-1)='';
      end
   end
   special_chars = ',{}()';
   cumidx=[];
   for i=1:length(special_chars)
      idx = findstr(datavec,special_chars(i));
      cumidx = [cumidx,idx];
   end
   datavec(cumidx) = blanks(length(cumidx));
   clear linefeeds;
%%
%% Close the file
%%
   fclose('all');
   if compressed==1; unix(['compress ',fname]); end;
   if compressed==2; unix(['gzip ',fname]); end;
%%
%%  Next, read in basic problem size parameters.
%%
   datavec = sscanf(datavec,'%f');
   if size(datavec,1) < size(datavec,2); datavec = datavec'; end;
   m = datavec(1);
   numblk  = datavec(2);
   blksize = datavec(2+[1:numblk]);
   if size(blksize,1) > size(blksize,2); blksize = blksize'; end
%%
%% Get input  b.
%%
   idxstrt = 2+numblk;
   b = datavec(idxstrt+[1:m]);
   idxstrt = idxstrt+m;
   b = -b;
%%
%% Construct blk
%%
   deblksize = 100;
   spblkidxtmp = find( (blksize>1) & (blksize < deblksize) );
   spblkidxtmp = sort(spblkidxtmp);
   deblkidx = find( (blksize<=1) | (blksize >= deblksize) );
   denumblk = length(deblkidx);
   linblkidx = zeros(1,denumblk);
   for p = 1:denumblk
      n = blksize(deblkidx(p));
      if (n > 1);
         blk{p,1} = 's'; blk{p,2} = n;
         n2 = n*(n+1)/2;
         At{p,1} = sparse(n2,m);
         C{p,1} = sparse(n,n);
      else
         linblkidx(p) = p;
         blk{p,1} = 'l'; blk{p,2} = abs(n);
         At{p,1} = sparse(abs(n),m);
         C{p,1} = sparse(abs(n),1);
      end
   end
   if ~isempty(spblkidxtmp)
      maxnumblk = 200;
      spnumblk = ceil(length(spblkidxtmp)/maxnumblk);
      for q = 1:spnumblk
         if (q < spnumblk)
            spblkidxall{q} = spblkidxtmp([(q-1)*maxnumblk+1: q*maxnumblk]);
         else
            spblkidxall{q} = spblkidxtmp([(q-1)*maxnumblk+1: length(spblkidxtmp)]);
         end
         tmp = blksize(spblkidxall{q});
         blk{denumblk+q,1} = 's';
         blk{denumblk+q,2} = tmp;
         n2 = sum(tmp.*(tmp+1))/2;
         At{denumblk+q,1} = sparse(n2,m);
         C{denumblk+q,1} = sparse(sum(tmp),sum(tmp));
      end
   else
      spnumblk = 0;
   end
   linblkidx(denumblk+[1:spnumblk]) = zeros(1,spnumblk);
%%
%% Construct single blocks of A,C
%%
   len = length(datavec);
   Y = reshape(datavec(idxstrt+1:len),5,(len-idxstrt)/5)';
   clear datavec;
   Y = sortrows(Y,[1 2]);
   matidx = [0; find(diff(Y(:,1)) ~= 0); size(Y,1)];
%%
   for k = 1:length(matidx)-1
      idx = [matidx(k)+1 : matidx(k+1)];
      Ytmp  = Y(idx,1:5);
      matno = Ytmp(1,1);
      Ytmp2 = Ytmp(:,2);
      for p = 1:denumblk
         n  = blksize(deblkidx(p));
         idx = find(Ytmp2 == deblkidx(p));
         ii = Ytmp(idx,3); jj = Ytmp(idx,4); vv =Ytmp(idx,5);
         len = length(idx);
         if (n > 1)
            idxtmp = find(ii > jj);
            if ~isempty(idxtmp);
               tmp = jj(idxtmp);
               jj(idxtmp) = ii(idxtmp); ii(idxtmp) = tmp;
            end
            tmp = -sparse(ii,jj,vv,n,n);
            tmp = tmp + triu(tmp,1)';
         else
            tmp = -sparse(ii,ones(len,1),vv,abs(n),1);
         end
         if (matno == 0)
            C{p,1} = tmp;
         else
            if (n > 1)
               At{p,1}(:,matno) = svec(blk(p,:),tmp,1);
            else
               At{p,1}(:,matno) = tmp;
            end
         end
      end
   end
%%
%% Construct big sparse block of A,C
%%
if (spnumblk > 0)
   Y1 = Y(:,1);
   diffY1 = find(diff([-1; Y1; inf]));
   for kk = 1:length(diffY1)-1
      idx = [diffY1(kk) : diffY1(kk+1)-1];
      matno = Y1(diffY1(kk));
      Ytmp  = Y(idx,1:5);
      Ytmp2 = Ytmp(:,2);
      maxYtmp2  = Ytmp2(length(Ytmp2));
      minYtmp2  = Ytmp2(1);
      diffYtmp2 = Ytmp2(find(diff([-1; Ytmp2])));
      for q = 1:spnumblk
         spblkidx    = spblkidxall{q};
         maxspblkidx = spblkidx(length(spblkidx));
         minspblkidx = spblkidx(1);
         count = 0;
         if (minYtmp2 <= maxspblkidx) & (maxYtmp2 >= minspblkidx)
            tmpblksize = blksize(spblkidx);
            n = sum(tmpblksize);
            cumspblksize = [0 cumsum(tmpblksize)];
            n2 = sum(tmpblksize.*(tmpblksize+1))/2;
            idx = zeros(n2,1); offset = zeros(n2,1);
            for t = [1:length(diffYtmp2)]
  	       p = find(spblkidx == diffYtmp2(t));
               if ~isempty(p)
	          idxtmp = find(Ytmp2 == spblkidx(p));
                  len = length(idxtmp);
     	          idx(count+[1:len]) = idxtmp;
                  offset(count+[1:len]) = cumspblksize(p);
                  count = count + len;
               end
            end
            idx = idx(1:count); offset = offset(1:count);
            ii = Ytmp(idx,3)+offset; jj = Ytmp(idx,4)+offset; vv = Ytmp(idx,5);
            idxtmp = find(ii > jj);
            if ~isempty(idxtmp);
               tmp = jj(idxtmp);
               jj(idxtmp) = ii(idxtmp); ii(idxtmp) = tmp;
            end
	    idxeq = find(ii==jj);
            tmp = spconvert([ii jj -vv; jj ii -vv; n n 0]) ...
                  + spconvert([ii(idxeq) jj(idxeq) vv(idxeq); n n 0]);
            if (matno == 0)
               C{denumblk+q,1} = tmp;
            else
               At{denumblk+q,1}(:,matno) = svec(blk(denumblk+q,:),tmp,1);
            end
         end
      end
   end
end
%%
%% put all linear blocks together as a single linear block
%%
   idx = find(linblkidx);
   if (length(idx) > 1)
      sdpidx = find(linblkidx==0);
      blktmp = 0; Atmp = []; Ctmp = [];
      for k = 1:length(idx)
          tmp = linblkidx(idx(k));
          blktmp = blktmp+blk{tmp,2};
          Atmp = [Atmp; At{tmp}];
          Ctmp = [Ctmp; C{tmp}];
      end
      At = At(sdpidx); C = C(sdpidx); blk = blk(sdpidx,:);
      len = length(sdpidx);
      blk(2:len+1,:) = blk;
      blk{1,1} = 'l'; blk{1,2} = blktmp;
      At(2:len+1,1) = At; C(2:len+1,1) = C;
      At{1,1} = Atmp; C{1,1} = Ctmp;
   end
%%******************************************************************


function Mh = hmxPlusMtimes(Mh,alpha,Ml,Mr)
%+========================================================================+
%|                                                                        |
%|         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       : hmxPlusMtimes.m                               |
%|    #    |   VERSION    : 0.40                                          |
%|   _#_   |   AUTHOR(S)  : Matthieu Aussal                               |
%|  ( # )  |   CREATION   : 14.03.2017                                    |
%|  / 0 \  |   LAST MODIF : 14.03.2018                                    |
%| ( === ) |   SYNOPSIS   : Sum and product of H-Matrix with the rule     |
%|  `---'  |                                                              |
%+========================================================================+

% Check dimensions
if (size(Mh,1) ~= size(Ml,1))
    error('hmxPlusMtimes.m : matrix dimensions must agree.')
end
if (size(Mh,2) ~= size(Mr,2))
    error('hmxPlusMtimes.m : matrix dimensions must agree.')
end
if (size(Ml,2) ~= size(Mr,1))
    error('hmxPlusMtimes.m : matrix dimensions must agree.')
end

%%% H-Matrix + alpha * H-Matrix * H-Matrix --> H-Matrix
if isa(Mh,'hmx') && isa(Ml,'hmx') && isa(Mr,'hmx')

    % H-Matrix + H-Matrix * H-Matrix --> H-Matrix   (recursion)
    if (Mh.typ==0) && (Ml.typ==0) && (Mr.typ==0)
        % First bloc : M1 -> M1 + alpha * (Ml1 * Mr1 + Ml2 * Mr3)
        Mh.chd{1} = hmxPlusMtimes(Mh.chd{1},alpha,Ml.chd{1},Mr.chd{1});
        Mh.chd{1} = hmxPlusMtimes(Mh.chd{1},alpha,Ml.chd{2},Mr.chd{3});

        % Second bloc : M2 -> M2 + alpha * (Ml1 * Mr2 + Ml2 * Mr4)
        Mh.chd{2} = hmxPlusMtimes(Mh.chd{2},alpha,Ml.chd{1},Mr.chd{2});
        Mh.chd{2} = hmxPlusMtimes(Mh.chd{2},alpha,Ml.chd{2},Mr.chd{4});

        % Third bloc : M3 -> M3 + alpha * (Ml3 * Mr1 + Ml4 * Mr3)
        Mh.chd{3} = hmxPlusMtimes(Mh.chd{3},alpha,Ml.chd{3},Mr.chd{1});
        Mh.chd{3} = hmxPlusMtimes(Mh.chd{3},alpha,Ml.chd{4},Mr.chd{3});

        % Fourth bloc : M4 -> M4 + alpha * (Ml3 * Mr2  + Ml4 * Mr4)
        Mh.chd{4} = hmxPlusMtimes(Mh.chd{4},alpha,Ml.chd{3},Mr.chd{2});
        Mh.chd{4} = hmxPlusMtimes(Mh.chd{4},alpha,Ml.chd{4},Mr.chd{4});

        % Fusion
        Mh = hmxFusion(Mh);

    % H-Matrix + H-Matrix * Compr --> H-Matrix
    elseif (Mh.typ==0) && (Ml.typ==0) && (Mr.typ==1)
        A  = alpha * ( Ml * Mr.dat{1} );
        B  = Mr.dat{2};
        Mh = hmxPlusAB(Mh,A,B);

    % H-Matrix + H-Matrix * Full --> unavailable
    elseif (Mh.typ==0) && (Ml.typ==0) && (Mr.typ==2)
        error('hmxPlusMtimes : unvailable case')

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

    % H-Matrix + Compr * H-Matrix --> H-Matrix
    elseif (Mh.typ==0) && (Ml.typ==1) && (Mr.typ==0)
        A  = alpha * Ml.dat{1};
        B  = Ml.dat{2} * Mr;
        Mh = hmxPlusAB(Mh,A,B);

    % H-Matrix + Compr * Compr --> H-Matrix
    elseif (Mh.typ==0) && (Ml.typ==1) && (Mr.typ==1)
        A  = alpha * Ml.dat{1};
        B  = (Ml.dat{2} * Mr.dat{1}) * Mr.dat{2};
        Mh = hmxPlusAB(Mh,A,B);

    % H-Matrix + Compr * Full --> H-Matrix
    elseif (Mh.typ==0) && (Ml.typ==1) && (Mr.typ==2)
        error('hmxPlusMtimes : unvailable case')

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

    % H-Matrix + Full * H-Matrix --> Full
    elseif (Mh.typ==0) && (Ml.typ==2)
        error('hmxPlusMtimes : unvailable case')

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

    % Compr + H-Matrix * H-Matrix --> H-Matrix
    elseif (Mh.typ==1) && (Ml.typ==0) && (Mr.typ==0)
        Mh.typ = 0;
        Mh.row = Ml.row;
        Mh.col = Mr.col;
        for i = 1:4
            Mh.chd{i} = hmx(Ml.chd{i}.pos{1},Mr.chd{i}.pos{2},...
                Mh.dat{1}(Ml.row{i},:) , Mh.dat{2}(:,Mr.col{i}) , ...
                Ml.tol ) ;
        end
        Mh.dat = [];
        Mh     = hmxPlusMtimes(Mh,alpha,Ml,Mr);

    % Compr + H-Matrix * Compr --> Compr
    elseif (Mh.typ==1) && (Ml.typ==0) && (Mr.typ==1)
        A  = alpha * (Ml * Mr.dat{1});
        B  = Mr.dat{2};
        Mh = hmxPlusAB(Mh,A,B);

    % Compr + H-Matrix * Full --> Full
    elseif (Mh.typ==1) && (Ml.typ==0) && (Mr.typ==2)
        error('hmxPlusMtimes : unvailable case')

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

    % Compr + Compr * H-Matrix --> Compr
    elseif (Mh.typ==1) && (Ml.typ==1) && (Mr.typ==0)
        A  = alpha * Ml.dat{1};
        B  = Ml.dat{2} * Mr;
        Mh = hmxPlusAB(Mh,A,B);

    % Compr + Compr * Compr --> Compr
    elseif (Mh.typ==1) && (Ml.typ==1) && (Mr.typ==1)
        A  = alpha * Ml.dat{1};
        B  = (Ml.dat{2} * Mr.dat{1}) * Mr.dat{2};
        Mh = hmxPlusAB(Mh,A,B);

    % Compr + Compr * Full --> Compr
    elseif (Mh.typ==1) && (Ml.typ==1) && (Mr.typ==2)
        A  = alpha * Ml.dat{1};
        B  = Ml.dat{2} * Mr.dat;
        Mh = hmxPlusAB(Mh,A,B);

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

    % Compr + Full * H-Matrix --> Full
    elseif (Mh.typ==1) && (Ml.typ==2) && (Mr.typ==0)
        error('hmxPlusMtimes : unvailable case')

    % Compr + Full * Compr --> Compr
    elseif (Mh.typ==1) && (Ml.typ==2) && (Mr.typ==1)
        A  = alpha * (Ml.dat * Mr.dat{1});
        B  = Mr.dat{2};
        Mh = hmxPlusAB(Mh,A,B);

    % Compr + Full * Full --> Full
    elseif (Mh.typ==1) && (Ml.typ==2) && (Mr.typ==2)
        Mh.dat = full(Mh) + alpha * (Ml.dat * Mr.dat);
        Mh.typ = 2;

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

    % Full + --- --> Full
    elseif (Mh.typ==2)
        Mh.dat = full(Mh) + alpha * (full(Ml) * full(Mr));
        Mh.typ = 2;

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

    % Unknown type
    else
        error('hmxPlusMtimes : unvailable case')
    end

%%% Unavailable
else
    error('hmxPlusMtimes : unvailable case')
end
end