function soITrans = findSoITransitions(initialState, maxSearchUT, soiSkipIds, massLoss, orbitDecay, celBodyData)
%findSoITransitions Summary of this function goes here
%   Detailed explanation goes here

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %findSoITransitions() returns a matrix of information that contains
    % data on upcoming SoI transitions for a given state.  SoI transition
    % information is given in the following format:
    %
    % [transType, transUT, fromSoI, toSoI, fromRx, fromRy, fromRz, fromVx, fromVy, fromVz, toRx, toRy, toRz, toVx, toVy, toVz]
    %
    % transType - type of SoI transition.  1 for upwards, -1 for downwards
    % transUT   - UT of the SoI transition
    % fromSoI   - ID number of the body transitioning out of
    % toSoI     - ID number of the body transitioning into
    %
    % fromRx    - The following three are the position components of the s/c prior to leaving, relative to the fromSoI body
    % fromRy
    % fromRz
    % fromVx    - The following three are the velocity components of the s/c prior to leaving, relative to the fromSoI body
    % fromVy
    % fromVz
    %
    % toRx    - The following three are the position components of the s/c after moving to new SoI, relative to the toSoI body
    % toRy
    % toRz
    % toVx    - The following three are the velocity components of the s/c after moving to new SoI, relative to the toSoI body
    % toVy
    % toVz
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    global num_SoI_search_revs use_selective_soi_search soi_search_tol num_soi_search_attempts_per_rev;

    if(isempty(use_selective_soi_search))
        use_selective_soi_search = 0;
    end

    if(isempty(num_SoI_search_revs))
        num_SoI_search_revs = 3;
    end

    if(isempty(soi_search_tol))
        soi_search_tol = 1E-12;
    end

    if(isempty(num_soi_search_attempts_per_rev))
        num_soi_search_attempts_per_rev = 1000;
    end

    ut = initialState(1);

    if(isempty(maxSearchUT))
        maxSearchUT = Inf;
    else
        if(maxSearchUT <= ut)
            maxSearchUT = Inf;
        end
    end

    bodyID = initialState(8);
    bodyInfo = getBodyInfoByNumber(bodyID, celBodyData);

    gmu = bodyInfo.gm;
    rVect = initialState(2:4)';
    vVect = initialState(5:7)';

    [sma, ecc, inc, raan, arg, truINI] = getKeplerFromState(rVect,vVect,gmu);
%     meanINI = computeMeanFromTrueAnom(truINI, ecc);
%     scBodyInfo = getBodyInfoStructFromOrbit([sma, ecc, inc, raan, arg, meanINI, ut]);

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %There are two cases to consider: moving up the heirarchy and moving
    % down the heirarchy.  For example:
    % Up Heirarchy: Kerbin -> Sun
    % Down Heirarchy Kerbin -> Mun
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %This segment looks for SoI transitions upwards.
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    upSoITrans = [];
    parentBodyInfo = bodyInfo.getParBodyInfo(celBodyData);
    if(~isempty(parentBodyInfo))
        soiRadius = getSOIRadius(bodyInfo, parentBodyInfo);
        if(ecc < 1)
            [rAp, ~] = computeApogeePerigee(sma, ecc);
        else
            rAp = Inf;
        end

        if(ecc >= 1 || rAp > soiRadius) %definite SoI transition upwards exists!
            if(abs(norm(rVect)-soiRadius)<0.001 && dot(rVect, vVect) > 0)
                truSoI = truINI;
                tempEventLog = initialState;
            else
                truSoI = computeTrueAFromRadiusEcc(soiRadius, sma, ecc);
                tempEventLog = ma_executeCoast_goto_tru(truSoI, initialState, -1, false, soiSkipIds, [], massLoss, orbitDecay, celBodyData);
            end

            upSoITransUT = tempEventLog(end,1);

            if(upSoITransUT <= maxSearchUT)
                [rVectUp, vVectUp] = convertRVVectOnUpwardsSoITransition(bodyInfo, celBodyData, upSoITransUT, tempEventLog(end,2:4), tempEventLog(end,5:7));

                upSoITrans = [1, upSoITransUT, bodyInfo.id, parentBodyInfo.id, ...
                              tempEventLog(end,2:7), ...
                              rVectUp', vVectUp'];
            else
                upSoITrans = [];
            end
        else
            upSoITrans = [];
        end
    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %This segment looks for SoI transitions downwards.  This is the
    % trickier of the two algorithms.
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    parentBodyInfo = bodyInfo.getParBodyInfo(celBodyData);
    [childBodies] = getChildrenOfParentInfo(celBodyData, lower(bodyInfo.name));
    downSoITrans = [];

    searchableChildBodies = KSPTOT_BodyInfo.empty(1,0);
    childBodySoIRadii = [];
    for(child = childBodies)
        childBodyInfo = child{1};

        if(use_selective_soi_search && ismember(childBodyInfo.id,soiSkipIds))
            continue;
        else
            searchableChildBodies(end+1) = childBodyInfo; %#ok<AGROW>
            childBodySoIRadii(end+1) = getSOIRadius(childBodyInfo, bodyInfo);
        end
    end

    if(not(isempty(searchableChildBodies)))
        meanMotion = computeMeanMotion(sma, gmu);
        odefun = @(ut, mean) soiSearchOdeFun(ut, mean, meanMotion);

        [maxSearchUTComputed, maxStepSize] = getMaxSoISearchTime(ut, sma, ecc, truINI, bodyInfo, gmu, parentBodyInfo, num_SoI_search_revs, num_soi_search_attempts_per_rev);
        if(isempty(maxSearchUT) || not(isfinite(maxSearchUT)) || isnan(maxSearchUT))
            maxSearchUT = maxSearchUTComputed;
        end

        tspan = [ut, maxSearchUT];
        if(isempty(maxSearchUT))
            fprintf('%0.9f - %0.9f - %0.9f - %0.9f - %0.9f - %0.9f - %0.9f\n\n', ut, sma, ecc, truINI, gmu, num_SoI_search_revs, num_soi_search_attempts_per_rev);
        end

        meanIni = computeMeanFromTrueAnom(truINI, ecc);
        if(ecc < 1)
            meanIni = AngleZero2Pi(meanIni);
        end
        y0 = meanIni;

        odeEvtFcn = @(ut,mean) getSoITransitionOdeEvents(ut, mean, sma, ecc, inc, raan, arg, bodyInfo, gmu, searchableChildBodies, childBodySoIRadii, celBodyData);
        options = odeset('RelTol',soi_search_tol, 'AbsTol',soi_search_tol, 'Events',odeEvtFcn, 'MaxStep',maxStepSize, 'Refine',1, 'InitialStep',maxStepSize);

        [~,~,te,~,ie] = ode113(odefun,tspan,y0,options);

        if(not(isempty(ie)))
            [crossingUT, I] = min(te);
            childBodyInfo = searchableChildBodies(ie(I));

            tempEventLog = ma_executeCoast_goto_ut(crossingUT, initialState, -1, false, soiSkipIds, massLoss,  orbitDecay, celBodyData);

            [rVectDown, vVectDown] = convertRVVectOnDownwardsSoITransition(childBodyInfo, celBodyData, crossingUT, tempEventLog(end,2:4), tempEventLog(end,5:7));

            if(dot(rVectDown, vVectDown) < 0)
                downSoITrans(end+1,:) = [-1, crossingUT, bodyInfo.id, childBodyInfo.id, ...
                                         tempEventLog(end,2:7), ...
                                         rVectDown', vVectDown'];
            end
        end
    end

    soITrans = [upSoITrans;downSoITrans];
end

function [maxSearchUT, maxStep] = getMaxSoISearchTime(utINI, sma, ecc, truINI, bodyInfo, gmu, parentBodyInfo, num_SoI_search_revs, num_soi_search_attempts_per_rev)
    numStepsPer = num_soi_search_attempts_per_rev;

    if(ecc < 1)
        oPeriod = computePeriod(sma,gmu);

        maxSearchUT = utINI + num_SoI_search_revs .* oPeriod;

        maxStep = abs(maxSearchUT - utINI)/num_SoI_search_revs/numStepsPer;
    else
        soiRadius = getSOIRadius(bodyInfo, parentBodyInfo);
        maxHypTru = AngleZero2Pi(real(computeTrueAFromRadiusEcc(soiRadius, sma, ecc)));

        meanMotion = computeMeanMotion(sma, gmu);
        meanIni = computeMeanFromTrueAnom(truINI, ecc);
        maxMean = computeMeanFromTrueAnom(maxHypTru, ecc);

        maxSearchUT = utINI + (maxMean - meanIni)./meanMotion;

        maxStep = abs(maxSearchUT - utINI)./numStepsPer;
    end
end

function meandot = soiSearchOdeFun(~, ~, meanMotion)
    meandot = meanMotion;
end

function [value, isterminal, direction] = getSoITransitionOdeEvents(ut, mean, sma, ecc, inc, raan, arg, bodyInfo, gmu, childBodies, searchableChildBodies, celBodyData)
    value = [];
    isterminal = [];
    direction = [];

    tru = computeTrueAnomFromMean(mean, ecc);
    [rVect, vVect] = getStatefromKepler(sma, ecc, inc, raan, arg, tru, gmu, false);

    for(i=1:length(childBodies)) %#ok<*NO4LP>
        childBodyInfo = childBodies(i);

        dVect = getAbsPositBetweenSpacecraftAndBody(ut, rVect, bodyInfo, childBodyInfo, celBodyData);
        distToChild = norm(dVect);

        rSOI = searchableChildBodies(i);

        val = distToChild - rSOI;

        value(end+1) = val; %#ok<AGROW>
        direction(end+1) = -1; %#ok<AGROW>
        isterminal(end+1) = 1; %#ok<AGROW>
    end
end

%     for(child = childBodies)
%         childBodyInfo = child{1};
%
%         if(use_selective_soi_search && ismember(childBodyInfo.id,soiSkipIds))
%             continue;
%         end
%
%         soiRadius = getSOIRadius(childBodyInfo, bodyInfo);
%         if(~isempty(parentBodyInfo))
%             soiRadiusParent = getSOIRadius(bodyInfo, parentBodyInfo);
%         else
%             soiRadiusParent = Inf;
%         end
%
%         [rAp, rPe] = computeApogeePerigee(sma, ecc);
%         if(ecc > 1)
%             rAp = Inf;
%         end
%
%         [rApBody, rPeBody] = computeApogeePerigee(childBodyInfo.sma, childBodyInfo.ecc);
%
%         %%%%%%%%%%%
%         %These are the two cases in which an SoI transition downwards is
%         %impossible:
%         % 1) S/C apogee is less than the child body perigee minus the SoI
%         %    radius (we don't get up the child body).
%         % 2) S/C perigee is greater than the child body apogee plus the SoI
%         %    radius (we don't get down to the child body).
%         % 3) Position vectors at the asc/desc nodes must be within some
%         %    small number of SoI radii.
%         %%%%%%%%%%%
%         if(rAp < rPeBody-soiRadius)
%             continue;
%         end
%         if(rPe > rApBody+soiRadius)
%             continue;
%         end
%
%         if(strict_SoI_search)
%             sma1=sma;
%             ecc1=ecc;
%             inc1=inc;
%             raan1=raan;
%             arg1=arg;
%
%             sma2=childBodyInfo.sma;
%             ecc2=childBodyInfo.ecc;
%             inc2=deg2rad(childBodyInfo.inc);
%             raan2=deg2rad(childBodyInfo.raan);
%             arg2=deg2rad(childBodyInfo.arg);
%
%             [TF] = intersectCheck(sma1, ecc1, inc1, raan1, arg1,...
%                                   sma2, ecc2, inc2, raan2, arg2,...
%                                   gmu, soiRadius);
%
%             if(TF == false)
%                 continue;
%             end
%         end
%
%         soiSf = 0;
%
%         distScChildBody = norm(getAbsPositBetweenSpacecraftAndBody(ut, rVect, scBodyInfo, childBodyInfo, celBodyData));
%         if(distScChildBody < (soiRadius-2*soiSf)) %The spacecraft starts out inside the SoI for some reason!
%                 [rVectDown, vVectDown] = convertRVVectOnDownwardsSoITransition(childBodyInfo, celBodyData, ut, initialState(end,2:4), initialState(end,5:7));
%                 if(dot(rVectDown,vVectDown) < 0) %we're already headed down, so we can transition downwards.  But if we're headed up at the SoI boundary, don't transition!  Will cause SoI dithering.
%                     downSoITrans(end+1,:) = [-1, ut, bodyInfo.id, childBodyInfo.id, ...
%                                              initialState(end,2:7), ...
%                                              rVectDown', vVectDown']; %#ok<AGROW>
%                 end
%         end
%
%         n1 = computeMeanMotion(sma, gmu);
%         n2 = computeMeanMotion(childBodyInfo.sma, gmu);
%         maxSearchRadius = min(rApBody+soiRadius,rAp);
%         minSearchRadius = max(rPeBody-soiRadius,rPe);
%         if(ecc < 1)
%             sPeriod = computeSynodicPeriod(n1, n2);
%             oPeriod = computePeriod(sma,gmu);
%             if(sPeriod / oPeriod > 3)
%                 sPeriod = 3*computePeriod(sma,gmu);
%             end
%             numPeriods = ceil(sPeriod/oPeriod);
%             numPeriods = num_SoI_search_revs;
%
%             truMax1=real(computeTrueAFromRadiusEcc(maxSearchRadius, sma, ecc));
%             truMin1=real(computeTrueAFromRadiusEcc(minSearchRadius, sma, ecc));
%
%             truMax2 = 2*pi-truMin1;
%             truMin2 = 2*pi-truMax1;
%
%             %%%%%%%%%Set 1%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%             MA1 = computeMeanFromTrueAnom(truMin1, ecc);
%             MA2 = computeMeanFromTrueAnom(truMax1, ecc);
%
%             dt = (MA1 - meanINI)/n1;
%             if(dt < 0)
% %                 if(meanINI>=MA1 && meanINI<=MA2)
% %                     dt = (2*pi-meanINI+MA1)/n1;
% %                 else
% %                     dt = (2*pi-meanINI+MA1)/n1;
% %                 end
%                 dt = (2*pi-meanINI+MA1)/n1;
%             end
%
%             minT = ut+dt-100;
%             if(minT < ut)
%                 minT = ut;
%             end
%
%             dMA = MA2-MA1;
%             sPeriod = dMA/n1;
%             tArr = [];
%             for(i=1:numPeriods)
%                 t1 = minT + (i-1)*oPeriod;
%                 t2 = minT + (i-1)*oPeriod + sPeriod + 100;
%
%                 if(t1 <= maxSearchUT && t2 <= maxSearchUT)
%                     tArr(end+1) = t1;
%                     tArr(end+1) = t2;
%                 elseif(t1 <= maxSearchUT && t2 > maxSearchUT)
%                     tArr(end+1) = t1;
%                     tArr(end+1) = maxSearchUT; %we don't want to exceed maxSearchUT
%                 else
%                     continue; %both t1 and t2 are greater than maxSearchUT, don't add them
%                 end
%             end
%
%             if(truINI >= truMin1 && truINI <= truMax1)
%                 tArr(end+1) = ut;
%                 tArr(end+1) = minT;
%             end
%
%             %%%%%%%%%Set 2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%             MA1 = computeMeanFromTrueAnom(truMin2, ecc);
%             MA2 = computeMeanFromTrueAnom(truMax2, ecc);
%
%             dt = (MA1 - meanINI)/n1;
%             if(dt < 0)
% %                 dt = 0;
%                 dt = (2*pi-meanINI+MA1)/n1;
%             end
%
%             minT = ut+dt-100;
%             if(minT < ut)
%                 minT = ut;
%             end
%
%             dMA = MA2-MA1;
%             sPeriod = dMA/n1;
%             for(i=1:numPeriods)
%                 t1 = minT + (i-1)*oPeriod;
%                 t2 = minT + (i-1)*oPeriod + sPeriod + 100;
%
%                 if(t1 <= maxSearchUT && t2 <= maxSearchUT)
%                     tArr(end+1) = t1;
%                     tArr(end+1) = t2;
%                 elseif(t1 <= maxSearchUT && t2 > maxSearchUT)
%                     tArr(end+1) = t1;
%                     tArr(end+1) = maxSearchUT; %we don't want to exceed maxSearchUT
%                 else
%                     continue; %both t1 and t2 are greater than maxSearchUT, don't add them
%                 end
%             end
%
%             if(truINI >= truMin2 && truINI <= truMax2)
%                 tArr(end+1) = ut;
%                 tArr(end+1) = minT;
%             end
%
%         else
%             iniHyTruMax1 = AngleZero2Pi(real(computeTrueAFromRadiusEcc(maxSearchRadius, sma, ecc)));
%             iniHyTruMin1 = AngleZero2Pi(real(computeTrueAFromRadiusEcc(minSearchRadius, sma, ecc)));
%
%             iniHyTruMax2 = -iniHyTruMin1;
%             iniHyTruMin2 = -iniHyTruMax1;
%
%             %%%%%%%%%Set 1%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%             MA1 = computeMeanFromTrueAnom(iniHyTruMin1, ecc);
%             MA2 = computeMeanFromTrueAnom(iniHyTruMax1, ecc);
%
%             dt = (MA1 - meanINI)/n1;
%             if(dt < 0)
%                 dt = 0;
%             end
%
%             dMA = MA2-MA1;
%             sPeriod = dMA/n1;
%
%             minT = ut+dt-100;
%             if(minT < ut)
%                 minT = ut;
%             end
%             tArr = linspace(minT, ut+dt+sPeriod+100, 2);
%
%             %%%%%%%%%Set 2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%             MA1 = computeMeanFromTrueAnom(iniHyTruMin2, ecc);
%             MA2 = computeMeanFromTrueAnom(iniHyTruMax2, ecc);
%
%             dt = (MA1 - meanINI)/n1;
%             if(dt < 0)
%                 dt = 0;
%             end
%
%             dMA = MA2-MA1;
%             sPeriod = dMA/n1;
%
%             minT = ut+dt-100;
%             if(minT < ut)
%                 minT = ut;
%             end
%
%             tArrTmp = linspace(minT, ut+dt+sPeriod+100, 2);
%             tArr(end+1) = min(tArrTmp);
%             tArr(end+1) = max(tArrTmp);
%         end
%
%         %These two lines sort the SoI calculations so that they're in
%         %time-order, meaning that earlier SoI transitions will not be
%         %missed if there are multiple possible SoI transitions.
%         tArr = sortrows(reshape(tArr, [2,length(tArr)/2])', 1);
%         tArr = reshape(tArr',1,[]);
%
%         findChildSoITransFunc = @(ut) isSoICrossing(ut, scBodyInfo, childBodyInfo, bodyInfo, soiRadius-soiSf, true, celBodyData);
%         findChildSoITransFuncNoAbs = @(ut) isSoICrossing(ut, scBodyInfo, childBodyInfo, bodyInfo, soiRadius-soiSf, false, celBodyData);
%         findChildSoITransFuncRealSoI = @(ut) isSoICrossing(ut, scBodyInfo, childBodyInfo, bodyInfo, soiRadius-10/1000, false, celBodyData);
%
%         options = optimset('TolX',soi_search_tol);
%         tolX2 = soi_search_tol;
%         for(i=1:length(tArr)/2) %#ok<*NO4LP>
%             tInds = [2*(i-1)+1, 2*i];
%
%             if(tArr(tInds(1)) >= tArr(tInds(2)))
%                 continue;
%             end
%
%             %%%%%%%%%
%             %This block of code is designed to weed out extraneous calls to
%             %fminbnd by avoiding looking for SoI transitions where the
%             %child body in question is clearly off on the other side of the
%             %orbit.
%             %%%%%%%%%
%             [rVectSc1] = getStateAtTime(scBodyInfo, tArr(tInds(1)), gmu);
%             [rVectSc2] = getStateAtTime(scBodyInfo, tArr(tInds(2)), gmu);
%
%             [rVectC1] = getStateAtTime(childBodyInfo, tArr(tInds(1)), gmu);
%             [rVectC2] = getStateAtTime(childBodyInfo, tArr(tInds(2)), gmu);
%
%             distT1 = norm(rVectSc1-rVectC1)/childBodyInfo.sma;
%             distT2 = norm(rVectSc2-rVectC2)/childBodyInfo.sma;
%
%             if(strict_SoI_search)
%                 smaFrac = 0.5;
%             else
%                 smaFrac = 60.0; %used to be 0.9
%             end
%
%             if(min(distT1, distT2) > smaFrac)
%                 if(strict_SoI_search)
%                     fprintf('While propagating Mission Architect orbit, strict SoI search setting skipped over a possible SoI transition between UT = %f sec and UT = %f sec.\n', tArr(tInds(1)), tArr(tInds(2)))
%                 end
%                 continue;
%             end
%
%             try
%                 minUT = max(tArr(tInds(1)), initialState(1));
%                 maxUT = tArr(tInds(2));
%
%                 if(minUT > maxUT)
%                     continue;
%                 end
%
%                 if(num_soi_search_attempts_per_rev > 1)
%                     startPts = linspace(minUT,maxUT,num_soi_search_attempts_per_rev+1);
%                 else
%                     startPts = [minUT,maxUT];
%                 end
%                 crossingUTs = NaN(length(startPts)-1,1);
%                 minDistFromSOI2s = crossingUTs;
%                 for(j=1:length(startPts)-1)
%                     minUT_Opt = startPts(j);
%                     maxUT_Opt = startPts(j+1);
%
%                     [crossingUTs(j), ~,~,~] = fminbnd(findChildSoITransFunc,minUT_Opt,maxUT_Opt, options);
%                     minDistFromSOI2s(j) = findChildSoITransFuncRealSoI(crossingUTs(j));
%
%                     if(minDistFromSOI2s(j) < 1)
%                         break; %we found SoI transition, no need to keep going
%                     end
%                 end
%
%                 [minDistFromSOI2, minI] = min(minDistFromSOI2s);
%                 crossingUT = crossingUTs(minI);
%
%                 if(minDistFromSOI2 > 100)
%                     continue;
%                 end
%             catch ME %#ok<*NASGU>
%                 continue;
%             end
%
%             if(abs(minDistFromSOI2) > 0)
%                 minUT = crossingUT-1200;
%                 maxUT = crossingUT+1200;
%                 unrefinedCrossingUt = crossingUT;
%
%                 [crossingUT,minDistFromSOI2, exitFlag] = bisection(findChildSoITransFuncNoAbs, minUT, maxUT, 0.0, tolX2);
%                 if(minDistFromSOI2 > 0.01)
%                     continue;
%                 end
%
%                 if(minDistFromSOI2 < 0.01 && isnan(crossingUT))
%                     options = optimset('Display','off', 'TolX',tolX2);
%                     [crossingUT,minDistFromSOI2, exitFlag] = fzero(findChildSoITransFuncNoAbs, unrefinedCrossingUt, options);
%
%                     if(minDistFromSOI2 > 0.01)
%                         continue;
%                     end
%                 end
%             end
%
%             if(isempty(crossingUT) || isnan(crossingUT) || ~isreal(crossingUT) || ~isfinite(crossingUT))
%                 continue;
%             end
%
%             if(abs(crossingUT-initialState(1)) <= 0.1)
%                 crossingUT = initialState(1);
%             elseif(abs(crossingUT-initialState(1)) > 0.1)
%                 if(crossingUT < initialState(1))
%                     continue;
%                 end
%             end
%
%             if(abs(maxSearchUT - crossingUT) <= 0.1)
%                 crossingUT = maxSearchUT;
%             elseif(abs(maxSearchUT - crossingUT) > 0.1)
%                 if(crossingUT > maxSearchUT)
%                     continue;
%                 end
%             end
%
%             tempEventLog = ma_executeCoast_goto_ut(crossingUT, initialState, -1, false, soiSkipIds, massLoss,  orbitDecay, celBodyData);
%
%             [rVectDown, vVectDown] = convertRVVectOnDownwardsSoITransition(childBodyInfo, celBodyData, crossingUT, tempEventLog(end,2:4), tempEventLog(end,5:7));
%             if(dot(rVectDown, vVectDown) > 0) %we found the outgoing part of the SoI transition
%                 crossingUTTemp = crossingUT;
%
%                 [smaTd, eccTd, ~, ~, ~, truTd] = getKeplerFromState(rVectDown, vVectDown, childBodyInfo.gm);
%                 [meanTd] = computeMeanFromTrueAnom(truTd, eccTd);
%                 [meanMotionTd] = computeMeanMotion(smaTd, childBodyInfo.gm);
% %                 [~,vVectTd] = getStatefromKepler(smaTd, eccTd, incTd, raanTd, argTd, 0.0, childBodyInfo.gm);
%
%                 maxUT = crossingUT - meanTd/meanMotionTd+100;
%                 minUT = crossingUT - 6*meanTd/meanMotionTd;
%                 minUT = max(minUT,initialState(1));
%
%                 if(maxUT < initialState(1))
%                     continue;
%                 end
%
%                 try
%                     [crossingUT, ~,~,~] = fminbnd(findChildSoITransFunc,minUT,maxUT, options);
%                     minDistFromSOI2 = findChildSoITransFuncRealSoI(crossingUT);
%                     if(minDistFromSOI2 > 100)
%                         continue;
%                     end
%                 catch ME %#ok<*NASGU>
%                     continue;
%                 end
%
%                 if(minDistFromSOI2 > 0)
%                     minUT = crossingUT-1200;
%                     minUT = max(minUT,initialState(1));
%                     maxUT = crossingUT+1200;
%                     [crossingUT,minDistFromSOI2] = bisection(findChildSoITransFuncRealSoI, minUT, maxUT, 0.0, tolX2);
%                     if(minDistFromSOI2 > 0.01)
%                         continue;
%                     end
%                 end
%
%                 if(isempty(crossingUT) || isnan(crossingUT) || ~isreal(crossingUT) || ~isfinite(crossingUT))
%                     continue;
%                 end
%
%                 if(abs(crossingUT-initialState(1)) <= 1)
%                     crossingUT = initialState(1);
%                 elseif(abs(crossingUT-initialState(1)) > 1)
%                     if(crossingUT < initialState(1))
%                         continue;
%                     end
%                 end
%
%                 if(abs(maxSearchUT - crossingUT) <= 1)
%                     crossingUT = maxSearchUT;
%                 elseif(abs(maxSearchUT - crossingUT) > 1)
%                     if(crossingUT > maxSearchUT)
%                         continue;
%                     end
%                 end
%
%                 tempEventLog = ma_executeCoast_goto_ut(crossingUT, initialState, -1, false, soiSkipIds, massLoss, orbitDecay, celBodyData);
%                 [rVectDown, vVectDown] = convertRVVectOnDownwardsSoITransition(childBodyInfo, celBodyData, crossingUT, tempEventLog(end,2:4), tempEventLog(end,5:7));
%             end
% %             if(abs(norm(rVectDown) - soiRadius) >= 0.01)
% %                 continue; %something wierd happened, go around
% %             end
%             if(crossingUT <= maxSearchUT)
%                 downSoITrans(end+1,:) = [-1, crossingUT, bodyInfo.id, childBodyInfo.id, ...
%                                          tempEventLog(end,2:7), ...
%                                          rVectDown', vVectDown']; %#ok<AGROW>
%             end
%             break;
%         end
%     end
%
%     soITrans = [upSoITrans;downSoITrans];
% end
%


function [Fi,Gi,details] = mpt_project_back_equality(Matrices,Fi,Gi,details,OriginalMatrices);
if isempty(Matrices.getback)
    return
else
    %Original solution given by S1*z+S2*x+S3
    S1 = Matrices.getback.S1;
    S2 = Matrices.getback.S2;
    S3 = Matrices.getback.S3;
    for i=1:length(Fi)
        if isempty(Fi{i})
            Fi{i} = S2;
            Gi{i} = S3;
        else
        Fi{i} = S1*Fi{i} + S2;
        Gi{i} = S1*Gi{i} + S3;
        end
    end
    if nargin>2
        if OriginalMatrices.qp
            for i=1:length(Fi)
                % FIX : Check this...
                details.Ai{i} = 0.5*Fi{i}'*OriginalMatrices.H*Fi{i} + 0.5*(OriginalMatrices.F*Fi{i}+Fi{i}'*OriginalMatrices.F') + OriginalMatrices.Y;
                details.Bi{i} = OriginalMatrices.Cf*Fi{i}+Gi{i}'*OriginalMatrices.F' + Gi{i}'*OriginalMatrices.H*Fi{i} + OriginalMatrices.Cx;
                details.Ci{i} = OriginalMatrices.Cf*Gi{i}+0.5*Gi{i}'*OriginalMatrices.H*Gi{i} + OriginalMatrices.Cc;
            end
        else
            for i=1:length(Fi)
                details.Bi{i} = OriginalMatrices.H*Fi{i} + OriginalMatrices.F;
                details.Ci{i} = OriginalMatrices.H*Gi{i};
            end
        end
    end
end


classdef iTreeNode < handle
    % iTreeNode A class to represent an internal (non-leaf) node of an
    % isolation tree
    %   An isolation tree is a type of binary tree.

    properties
        SplitAttribute
        SplitValue
    end

    properties(SetAccess = private)
      Left = iTreeNode.empty;
      Right = iTreeNode.empty;
   end

    methods
        function node = iTreeNode(splitatt, splitvalue)
            % Construct an iTreeNode object.
            if nargin > 0
                node.SplitAttribute = splitatt;
                node.SplitValue = splitvalue;
            end
        end

        function insertLeft(parentNode, newNode)
            parentNode.Left = newNode;
        end

        function insertRight(parentNode, newNode)
            parentNode.Right = newNode;
        end

        function leftorright = compare(node, x)
            leftorright = x(node.SplitAttribute) >= node.SplitValue;
        end
    end

end


function k = biasKernDiagCompute(kern, x)

% BIASKERNDIAGCOMPUTE Compute diagonal of BIAS kernel.
% FORMAT
% DESC computes the diagonal of the kernel matrix for the bias kernel given a design matrix of inputs.
% ARG kern : the kernel structure for which the matrix is computed.
% ARG x : input data matrix in the form of a design matrix.
% RETURN k : a vector containing the diagonal of the kernel matrix
% computed at the given points.
%
% SEEALSO : biasKernParamInit, kernDiagCompute, kernCreate, biasKernCompute
%
% COPYRIGHT : Neil D. Lawrence, 2004, 2005, 2006

% KERN

k = repmat(kern.variance, size(x, 1), 1);


%% Reading and writing midi files
% This short demo will show you how to read in a midi file, do some simple
% manipulation, and then write out new modified files.
%
% Before you try these function, you will need to make sure that
% KaraokeMidiJava.jar can be found by matlab.  The easiest way to do this
% is to edit Matlab's |classpath.txt| and add the path to the midi jar.
% You will, I believe, need to restart Matlab once you have updated the
% classpath file.
%
% |readmidi_java.m| and |writemidi_java.m| were inspired by the |readmidi.m| and
% |writemidi.m| of the
% <https://www.jyu.fi/hum/laitokset/musiikki/en/research/coe/materials/miditoolbox/ Midi Toolbox>.
% My versions can take the same inputs, but also
% have a few extensions, which you can find by reading the help information
% for each function.
%

%% Reading in a midi file
% First read in the file, which is "The sun, whose rays are all ablaze"
% from _The Mikado_ by Gilbert and Sullivan.  (Thanks to the
% <http://math.boisestate.edu/gas/mikado/html/index.html Gilbert & Sullivan archives> for this file!)

% Read a midi file into a note matrix, getting the optional
% track column.  (Note that .kar files are midi karaoke files.  They use
% exactly the same file format as .mid files.  The .kar basically just
% indicates that this is a midi file which contains midi lyric messages.)
%
% The columns are:
%       (1) - note start in beats
%       (2) - note duration in beats
%       (3) - channel
%       (4) - midi pitch (60 --> C4 = middle C)
%       (5) - velocity
%       (6) - note start in seconds
%       (7) - note duration in seconds
note_matrix = readmidi_java('202.kar');

% see how many notes we have
fprintf('Number of notes: %d\n', size(note_matrix,1));
% plot the note onsets
figure(1);
clf;
plot(note_matrix(:,1));
ylabel('Note onset (beats)');
xlabel('Note index');
title('Note onsets in beats');
figure(2);
clf;
plot(note_matrix(:,6));
ylabel('Note onset (seconds)');
xlabel('Note index');
title('Note onsets in seconds');

%% Writing out a midi file
% Now write out a couple of midi files that just contain the notes from the
% vocals melody.

% The vocal melody is in channel 3.
vocal_matrix = note_matrix(note_matrix(:,3) == 3,:);
% Get the tempo information from 202.kar
tempos = get_tempos('202.kar');
fprintf('Number of tempos: %d\n', size(tempos,1));

% Write out a midi file that preserves the beat information.  Use the last
% tempo from 202.kar
writemidi_java(vocal_matrix,'just_vocals_beats.mid',120,tempos(end,1));
% Write out another version that preserves the timing.  This may be helpful
% if you want to synthesize the file.  202.kar has
% multiple tempo changes, so specifying a single tempo in writemidi_java
% won't help.  This function writes a midi file that preserves the times in
% the note matrix (columns 6 & 7), but not the beats.
writemidi_seconds(vocal_matrix,'just_vocals_seconds.mid');

%%  Checking written midi file
% Read the vocal file in and make sure that it matches what we wrote.
% The |just_vocals_beats.mid| file should have preserved the beat information
% in |note_matrix|, but the timing information will be wrong because of the
% tempo changes in |202.kar|.  The |just_vocals_seconds.mid| file should do the
% opposite.

vm_beats = readmidi_java('just_vocals_beats.mid');
vm_seconds = readmidi_java('just_vocals_seconds.mid');

figure(1);
clf
plot(vocal_matrix(:,1), 'b.-');
hold on;
plot(vm_beats(:,1),'r');
plot(vm_seconds(:,1),'g');
legend('manipulated note matrix', 'correct beats matrix',...
    'correct times matrix','Location','NorthWest');
legend('boxoff');
ylabel('Note onset (beats)');
xlabel('Note index');
title('Vocal line notes in beats');

figure(2);
clf
plot(vocal_matrix(:,6), 'b.-');
hold on;
plot(vm_beats(:,6),'r');
plot(vm_seconds(:,6),'g');
legend('manipulated note matrix', 'correct beats matrix',...
    'correct times matrix','Location','NorthWest');
legend('boxoff');
ylabel('Note onset (seconds)');
xlabel('Note index');
title('Vocal line notes in seconds');

% 2010-05-03 Christine Smit csmit@ee.columbia.edu


function results = PTKGetRadiusForAirways(centreline_results, lung_image, radius_approximation, reporting, figure_airways_3d)
    % PTKGetRadiusForAirways. Computes the radius for a segmented airway tree.
    %
    %
    %
    %     Licence
    %     -------
    %     Part of the TD Pulmonary Toolkit. https://github.com/tomdoel/pulmonarytoolkit
    %     Author: Tom Doel, 2012.  www.tomdoel.com
    %     Distributed under the GNU GPL v3 licence. Please see website for details.
    %

    reporting.ShowProgress('Computing radius for each branch');

    [radius_results, skeleton_tree] = GetRadius(lung_image, centreline_results.IntermediateAirwaySkeleton, radius_approximation, reporting, figure_airways_3d);
    centreline_tree_model = PTKTreeModel.CreateFromSkeletonTree(skeleton_tree, lung_image);

    results = rmfield(centreline_results, 'IntermediateAirwaySkeleton');
    results.AirwayCentrelineTree = centreline_tree_model;
end

function [results, airway_skeleton] = GetRadius(lung_image, airway_skeleton, radius_approximation, reporting, figure_airways_3d)
    results = {};

    number_of_segments = airway_skeleton.CountBranches;
    segments_done = 0;

    lung_image_as_double = lung_image.BlankCopy;
    lung_image_as_double.ChangeRawImage(double(lung_image.RawImage));

    segments = airway_skeleton.GetBranchesAsListByGeneration;

    for next_segment = segments
        reporting.UpdateProgressValue(round(100*segments_done/number_of_segments));
        segments_done = segments_done + 1;

        next_result = PTKComputeRadiusForBranch(next_segment, lung_image_as_double, radius_approximation, figure_airways_3d, reporting);
        results{end+1} = next_result;

        next_segment.Radius = next_result.Radius;
        next_segment.WallThickness = next_result.WallThickness;

    end
end


%% clear the workspace and select data
clear; clc; close all;

%% choose data
neuron = Sources2D();
nam = './data_2p.tif';          % this demo data is very small, here we just use it as an example
nam = neuron.select_data(nam);  %if nam is [], then select data interactively

%% parameters
% -------------------------    COMPUTATION    -------------------------  %
pars_envs = struct('memory_size_to_use', 8, ...   % GB, memory space you allow to use in MATLAB
    'memory_size_per_patch', 0.3, ...   % GB, space for loading data within one patch
    'patch_dims', [30, 40]);  %GB, patch size

% -------------------------      SPATIAL      -------------------------  %
gSig = 0.5;           % pixel, gaussian width of a gaussian kernel for filtering the data. 0 means no filtering
gSiz = 10;          % pixel, neuron diameter
ssub = 1;           % spatial downsampling factor
with_dendrites = false;   % with dendrites or not
if with_dendrites
    % determine the search locations by dilating the current neuron shapes
    updateA_search_method = 'dilate';  %#ok<UNRCH>
    updateA_bSiz = 20;
    updateA_dist = neuron.options.dist;
else
    % determine the search locations by selecting a round area
    updateA_search_method = 'ellipse'; %#ok<UNRCH>
    updateA_dist = 5;
    updateA_bSiz = neuron.options.dist;
end
spatial_constraints = struct('connected', true, 'circular', false);  % you can include following constraints: 'circular'

% -------------------------      TEMPORAL     -------------------------  %
Fs = 5;             % frame rate
tsub = 1;           % temporal downsampling factor
deconv_options = struct('type', 'ar1', ... % model of the calcium traces. {'ar1', 'ar2'}
    'method', 'foopsi', ... % method for running deconvolution {'foopsi', 'constrained', 'thresholded'}
    'smin', -3, ...         % minimum spike size. When the value is negative, the actual threshold is abs(smin)*noise level
    'optimize_pars', true, ...  % optimize AR coefficients
    'optimize_b', true);% optimize the baseline);
nk = 1;             % detrending the slow fluctuation. usually 1 is fine (no detrending)
                    % when changed, try some integers smaller than total_frame/(Fs*30)
detrend_method = 'local_min';  % compute the local minimum as an estimation of trend.

% -------------------------     BACKGROUND    -------------------------  %
bg_model = 'svd';  % model of the background {'ring', 'svd'(default), 'nmf'}
nb = 1;             % number of background sources for each patch (only be used in SVD and NMF model)
ring_radius = 13;  % when the ring model used, it is the radius of the ring used in the background model.
                    %otherwise, it's just the width of the overlapping area

% -------------------------      MERGING      -------------------------  %
show_merge = false;  % if true, manually verify the merging step
merge_thr = 0.7;     % thresholds for merging neurons; [spatial overlap ratio, temporal correlation of calcium traces, spike correlation]
method_dist = 'max';   % method for computing neuron distances {'mean', 'max'}
dmin = 10;       % minimum distances between two neurons. it is used together with merge_thr
dmin_only = 2;  % merge neurons if their distances are smaller than dmin_only.

% -------------------------  INITIALIZATION   -------------------------  %
K = [];             % maximum number of neurons per patch. when K=[], take as many as possible
min_corr = 0.7;     % minimum local correlation for a seeding pixel
min_pnr =10;       % minimum peak-to-noise ratio for a seeding pixel
min_pixel = 2^2;      % minimum number of nonzero pixels for each neuron
bd = 0;             % number of rows/columns to be ignored in the boundary (mainly for motion corrected data)
frame_range = [];   % when [], uses all frames
save_initialization = false;    % save the initialization procedure as a video.
use_parallel = true;    % use parallel computation for parallel computing
show_init = true;   % show initialization results
choose_params = false; % manually choose parameters
center_psf = false;  % set the value as true when the background fluctuation is large (usually 1p data)
                    % set the value as false when the background fluctuation is small (2p)

% ----------------------   MANUAL INTERVENTION  --------------------  %
with_manual_intervention = false;

% -------------------------  FINAL RESULTS   -------------------------  %
save_demixed = true;    % save the demixed file or not
kt = 1;                 % frame intervals

% -------------------------    UPDATE ALL    -------------------------  %
neuron.updateParams('gSig', gSig, ...       % -------- spatial --------
    'gSiz', gSiz, ...
    'ring_radius', ring_radius, ...
    'ssub', ssub, ...
    'search_method', updateA_search_method, ...
    'bSiz', updateA_bSiz, ...
    'dist', updateA_bSiz, ...
    'spatial_constraints', spatial_constraints, ...
    'tsub', tsub, ...                       % -------- temporal --------
    'deconv_options', deconv_options, ...    '
    'nk', nk, ...
    'detrend_method', detrend_method, ...
    'background_model', bg_model, ...       % -------- background --------
    'nb', nb, ...
    'ring_radius', ring_radius, ...
    'merge_thr', merge_thr, ...             % -------- merging ---------
    'dmin', dmin, ...
    'method_dist', method_dist, ...
    'min_corr', min_corr, ...               % ----- initialization -----
    'min_pnr', min_pnr, ...
    'min_pixel', min_pixel, ...
    'bd', bd, ...
    'center_psf', center_psf);
neuron.Fs = Fs;

%% distribute data and be ready to run source extraction
neuron.getReady(pars_envs);

%% initialize neurons from the video data within a selected temporal range
if choose_params
    % change parameters for optimized initialization
    [gSig, gSiz, ring_radius, min_corr, min_pnr] = neuron.set_parameters();
end

[center, Cn, PNR] = neuron.initComponents_parallel(K, frame_range, save_initialization, use_parallel);
if show_init
    figure;
    imagesc(Cn, [0, 1]); colormap gray;
    hold on;
    plot(center(:, 2), center(:, 1), '.r', 'markersize', 10);
end
neuron_init = neuron.copy();

%% estimation of background components
neuron.update_background_parallel(use_parallel);

%% pick neurons from the residual
[center_res, Cn_res, PNR_res] =neuron.initComponents_residual_parallel(K, save_initialization, use_parallel);
neuron_init_res = neuron.copy();

%% estimate the temporal components
neuron.update_temporal_parallel(use_parallel);

%% estimate the spatial components
neuron.update_spatial_parallel(use_parallel);

%% deal with bad neurons
tags = neuron.tag_neurons_parallel();  % find neurons with fewer nonzero pixels than min_pixel and silent calcium transients
neuron.delete(tags>0);

%% merge neurons
neuron.merge_neurons_dist_corr(show_merge);
neuron.merge_close_neighbors(show_merge, dmin_only);

%% add a manual intervention and run the whole procedure for a second time
neuron.orderROIs('snr');   % order neurons in different ways {'snr', 'decay_time', 'mean', 'circularity'}
if with_manual_intervention
    neuron.viewNeurons([], neuron.C_raw);
end
neuron.update_background_parallel(use_parallel);
neuron.update_temporal_parallel(use_parallel);
neuron.update_spatial_parallel(use_parallel);

K = size(neuron.A,2);
tags = neuron.tag_neurons_parallel();  % find neurons with fewer nonzero pixels than min_pixel and silent calcium transients
neuron.delete(tags>0);
neuron.merge_neurons_dist_corr(show_merge);
neuron.merge_close_neighbors(show_merge, dmin_only);
if K~=size(neuron.A,2)
    neuron.update_temporal_parallel(use_parallel);
    neuron.update_spatial_parallel(use_parallel);
    neuron.update_background_parallel(use_parallel);
end

%% save the workspace for future analysis
neuron.save_workspace();

%% show neuron contours
Coor = neuron.show_contours();

%% create a video for displaying the
amp_ac = 5000;
range_ac = [0, 5000];
range_Y = [0, 10000];
neuron.show_demixed_video(save_demixed, kt, [], amp_ac, range_ac, range_Y);

%% save neurons shapes
neuron.save_neurons();

%% save neurons shapes
neuron.save_neurons();


function [g1, g2] = disimXrbfKernGradient(disimKern, rbfKern, t1, t2, covGrad)

% DISIMXRBFKERNGRADIENT Compute gradient between the DISIM and RBF kernels.
% FORMAT
% DESC computes the gradient of an objective function with respect
% to cross kernel terms between DISIM and RBF kernels for
% the multiple output kernel.
% ARG disimKern : the kernel structure associated with the DISIM
% kernel.
% ARG rbfKern : the kernel structure associated with the RBF
% kernel.
% ARG t : inputs for which kernel is to be computed.
% RETURN g1 : gradient of objective function with respect to kernel
% parameters of DISIM kernel.
% RETURN g2 : gradient of objective function with respect to kernel
% parameters of RBF kernel.
%
% FORMAT
% DESC computes the gradient of an objective function with respect
% to cross kernel terms between DISIM and RBF kernels for
% the multiple output kernel.
% ARG disimKern : the kernel structure associated with the DISIM
% kernel.
% ARG rbfKern : the kernel structure associated with the RBF
% kernel.
% ARG t1 : row inputs for which kernel is to be computed.
% ARG t2 : column inputs for which kernel is to be computed.
% RETURN g1 : gradient of objective function with respect to kernel
% parameters of DISIM kernel.
% RETURN g2 : gradient of objective function with respect to kernel
% parameters of RBF kernel.
%
% SEEALSO : multiKernParamInit, multiKernCompute, disimKernParamInit, rbfKernParamInit
%
% COPYRIGHT : Neil D. Lawrence, 2006
%
% COPYRIGHT : Antti Honkela, 2007-2009

% KERN

arg{1} = t1;
if nargin < 5
  covGrad = t2;
  t2 = t1;
else
  arg{2} = t2;
end
if size(t1, 2) > 1 | size(t2, 2) > 1
  error('Input can only have one column');
end
if disimKern.inverseWidth ~= rbfKern.inverseWidth
  error('Kernels cannot be cross combined if they have different inverse widths.')
end
if disimKern.rbf_variance ~= rbfKern.variance
  error('Kernels cannot be cross combined if they have different RBF variances.');
end

dim1 = size(t1, 1);
dim2 = size(t2, 1);
t1Mat = t1(:, ones(1, dim2));
t2Mat = t2(:, ones(1, dim1))';
diffT = (t1Mat - t2Mat);
l = sqrt(2/disimKern.inverseWidth);
l2 = l*l;
C_0 = sqrt(disimKern.di_variance);
C_i = sqrt(disimKern.variance);
C_j = rbfKern.variance;
D_i = disimKern.decay;
delta = disimKern.di_decay;

invLDiffT = 1/l*diffT;
halfLD_i = 0.5*l*D_i;
halfLDelta = 0.5*l*delta;

prefact = C_0 * C_i * C_j * sqrt(pi)/2 * l;

lnCommon = - log(delta - D_i);
[lnPart1, sign1] = lnDiffErfs(halfLDelta - invLDiffT, halfLDelta + t2Mat/l);
[lnPart2, sign2] = lnDiffErfs(halfLD_i + t2Mat/l, halfLD_i - invLDiffT);

lnFact1 = halfLDelta^2 - delta * diffT;
lnFact2 = halfLD_i^2 - D_i * diffT;

k = sign1 .* exp(lnCommon + lnFact1 + lnPart1) ...
    + sign2 .* exp(lnCommon + lnFact2 + lnPart2);

k = 0.5*sqrt(disimKern.variance)*sqrt(disimKern.di_variance)*rbfKern.variance*k*sqrt(pi)*l;

[dlnPart1, m1] = gradLnDiffErfs(halfLDelta - invLDiffT, halfLDelta + t2Mat/l, ...
				l/2, l/2);
[dlnPart2, m2] = gradLnDiffErfs(halfLD_i + t2Mat/l, halfLD_i - invLDiffT, ...
				l/2, l/2);

dK_dD = k .* (1./(delta-D_i)) ...
	+ prefact ...
	.* ((l*halfLD_i - diffT) .* sign2 .* exp(lnCommon + lnFact2 + lnPart2) ...
	    + dlnPart2 .* exp(lnCommon + lnFact2 - m2));
dk_dD = sum(sum(dK_dD.*covGrad));

dK_ddelta = k .* (-1./(delta-D_i)) ...
    + prefact ...
    .* ((l*halfLDelta - diffT) .* sign1 .* exp(lnCommon + lnFact1 + lnPart1) ...
	+ dlnPart1 .* exp(lnCommon + lnFact1 - m1));
dk_ddelta = sum(sum(dK_ddelta.*covGrad));

[dlnPart1, m1] = gradLnDiffErfs(halfLDelta - invLDiffT, halfLDelta + t2Mat/l, ...
				delta/2 + invLDiffT/l, delta/2 - t2Mat/l2);
[dlnPart2, m2] = gradLnDiffErfs(halfLD_i + t2Mat/l, halfLD_i - invLDiffT, ...
				D_i/2 - t2Mat/l2, D_i/2 + invLDiffT/l);

dK_dl = k/l ...
	+ prefact ...
	.* (delta*halfLDelta .* sign1 .* exp(lnCommon + lnFact1 + lnPart1) ...
	    + D_i*halfLD_i .* sign2 .* exp(lnCommon + lnFact2 + lnPart2) ...
	    + dlnPart1 .* exp(lnCommon + lnFact1 - m1) ...
	    + dlnPart2 .* exp(lnCommon + lnFact2 - m2));
dk_dl = sum(sum(dK_dl.*covGrad));

dk_dC_i = sum(sum(k.*covGrad))/C_i;
dk_dC_0 = sum(sum(k.*covGrad))/C_0;
dk_dRbfVariance = sum(sum(k.*covGrad))/rbfKern.variance;

dk_dinvWidth = -0.5*sqrt(2)/(disimKern.inverseWidth* ...
                             sqrt(disimKern.inverseWidth))*dk_dl;
dk_dDisimVariance = dk_dC_i*0.5/C_i;
dk_dDIVariance = dk_dC_0*0.5/C_0;

% only pass the gradient with respect to the inverse width to one
% of the gradient vectors ... otherwise it is counted twice.
g1 = real([dk_ddelta dk_dinvWidth dk_dDIVariance dk_dD dk_dDisimVariance 0]);
g2 = real([0 dk_dRbfVariance]);


% eeg_lat2point() - convert latencies in time units relative to the
%                   time locking event of an eeglab() data epoch to
%                   latencies in data points (assuming concatenated epochs).
% Usage:
%       >> [newlat] = eeg_lat2point( lat_array, epoch_array,...
%                                 srate, timelimits, timeunit);
%       >> [newlat] = eeg_lat2point( lat_array, epoch_array,...
%                                 srate, timelimits, '','outrange',1);
% Inputs:
%   lat_array   - latency array in 'timeunit' units (see below)
%   epoch_array - epoch number for each latency
%   srate       - data sampling rate in Hz
%   timelimits  - [min max] epoch timelimits in 'timeunit' units (see below)
%   timeunit    - time unit relative to seconds. Default is 1 = seconds.
%
% Optional inputs:
%   outrange    - [1/0] Replace the points out of the range with the value of
%                 the maximun point in the valid range or raise an error.
%                 Default [1] : Replace point.
%
% Outputs:
%   newlat      - converted latency values in points assuming concatenated
%                 data epochs (see eeglab() event structure)
%   flag        - 1 if any point out of range was replaced.
%
% Author: Arnaud Delorme, CNL / Salk Institute, 2 Mai 2002
%
% See also: eeg_point2lat(), eeglab()

% Copyright (C) 2 Mai 2002 Arnaud Delorme, Salk Institute, arno@salk.edu
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 2 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program; if not, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

function [newlat,flag] = eeg_lat2point( lat_array, epoch_array, srate, timewin, timeunit, varargin);
% -------------------------------------------------------------------------
try
    options = varargin;
    if ~isempty( varargin ),
        for i = 1:2:numel(options)
            g.(options{i}) = options{i+1};
        end
    else g= []; end;
catch
    error('std_checkdatasession() error: calling convention {''key'', value, ... } error');
end;
 try, g.outrange; catch, g.outrange = 1; end; %

 flag = 0;
% -------------------------------------------------------------------------

if nargin <4
    help eeg_lat2point;
    return;
end;
if nargin <5 | isempty(timeunit)
	timeunit = 1;
end;

if length(lat_array) ~= length(epoch_array)
	if length(epoch_array)~= 1
		disp('eeg_lat2point: latency and epochs must have the same length'); return;
	else
		epoch_array = ones(1,length(lat_array))*epoch_array;
	end;
end;
if length(timewin) ~= 2
    disp('eeg_lat2point: timelimits must have length 2'); return;
end;
if iscell(epoch_array)
	epoch_array = [ epoch_array{:} ];
end;
if iscell(lat_array)
	lat_array = [ lat_array{:} ];
end

timewin = timewin*timeunit;
pnts = (timewin(2)-timewin(1))*srate+1;
newlat  = (lat_array*timeunit-timewin(1))*srate+1 + (epoch_array-1)*pnts;

% Detecting points out of range (RMC)
% Note: This is neccesary since the double precision  multiplication could lead to the
% shifting in one sample out of the valid range

if  and(~isempty(newlat),~isempty(epoch_array)) && max(newlat(:)) > max((epoch_array)*pnts)
    if g.outrange == 1
        IndxOut = find(newlat(:) > max((epoch_array)*pnts));
        newlat(IndxOut) = max((epoch_array)*pnts);
        flag = 1;
        warning('eeg_lat2point(): Points out of range detected. Points replaced with maximum value');
    elseif g.outrange == 0
        error('Error in eeg_lat2point(): Points out of range detected');
    end
end


%NEWJOR Create an Jordan backpropagation network.
%
%  Syntax
%
%    net = newjor(P,T,[S1...S(N-l)],{TF1...TFN},BTF,BLF,PF,IPF,OPF,DDF)
%
%  Description
%
%     NET = NEWJOR(P,T,[S1...SNl],{TF1...TFN},BTF,BLF,PF,IPF,OPF,DDF) takes,
%      P  - RxQ1 matrix of Q1 representative R-element input vectors.
%      T  - SNxQ2 matrix of Q2 representative SN-element target vectors.
%      Si  - Sizes of N-1 hidden layers, S1 to S(N-1), default = [].
%            (Output layer size SN is determined from T.)
%      TFi - Transfer function of ith layer. Default is 'tansig' for
%            hidden layers, and 'purelin' for output layer.
%      BTF - Backprop network training function, default = 'traingdx'.
%      BLF - Backprop weight/bias learning function, default = 'learngdm'.
%      PF  - Performance function, default = 'mse'.
%      IPF - Row cell array of input processing functions.
%            Default is {'fixunknowns','removeconstantrows','mapminmax'}.
%      OPF - Row cell array of output processing functions.
%            Default is {'removeconstantrows','mapminmax'}.
%      DDF - Data division function, default = 'dividerand';
%    and returns an N-layer Jordan network.
%
%    The training function BTF can be any of the backprop training
%    functions such as TRAINGD, TRAINGDM, TRAINGDA, TRAINGDX, etc.
%
%    *WARNING*: Algorithms which take large step sizes, such as TRAINLM,
%    and TRAINRP, etc., are not recommended for Jordan networks.  Because
%    of the delays in Jordan networks the gradient of performance used
%    by these algorithms is only approximated making learning difficult
%    for large step algorithms.
%
%    The learning function BLF can be either of the backpropagation
%    learning functions such as LEARNGD, or LEARNGDM.
%
%    The performance function can be any of the differentiable performance
%    functions such as MSE or MSEREG.
% 	Mark Beale, 11-31-97
% 	Copyright 1992-2008 The MathWorks, Inc.
% 	$Revision: 1.1.6.9 $  $Date: 2009/08/08 01:11:34 $

function net = newjor(varargin)

% Arnav Goel, 11-06-12
% Copyright 2012 BITS Pilani KK Birla Goa Campus
% $Revision 0.0.3 $Date 21-11-12

%This portion of the code refers to error declaration of the code inorder
%to make sure that the no of the arguments and the nature of the arguments
%is compatible to the NNT ver > 5.0 for the jordan_p1

if nargin < 1, error('NNET:Arguments','Not enough input arguments'),end

v1 = varargin{1};
if isa(v1,'cell'), v1 = cell2mat(v1); end
v2 = varargin{2};
if nargin > 2, v3 = varargin{3}; end

if (nargin<= 6) && (size(v1,2)==2) && (~iscell(v2)) && (size(v2,1)==1) && ((nargin<3)||iscell(v3))
  nntobsu(mfilename,['See help for ' upper(mfilename) ' to update calls to the new argument list.']);
  net = jordan_p0(varargin{:});
else
  net = jordan_p1(varargin{:});
end

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

%this portion of the code is the main part with designing the Architecture,
%Simulation, Adaptation, Training & Initialization

function net = jordan_p1(p,t,s,tf,btf,blf,pf,ipf,tpf,ddf)
if nargin < 1, error('NNET:Arguments','Not enough arguments.'),end

% Defaults
if (nargin < 3), s = []; end
if (nargin < 4), tf = {}; end
if (nargin < 5), btf = 'traingdx'; end
if (nargin < 6), blf = 'learngdm'; end
if (nargin < 7), pf = 'mse'; end
if (nargin < 8), ipf = {'fixunknowns','removeconstantrows','mapminmax'}; end
if (nargin < 9), tpf = {'removeconstantrows','mapminmax'}; end
if (nargin < 10), ddf = 'dividerand'; end

% Format
if isa(p,'cell'), p = cell2mat(p); end
if isa(t,'cell'), t = cell2mat(t); end

% Error checking
if (~isa(p,'double')) || ~isreal(p)
  error('NNET:Arguments','Inputs are not a matrix or cell array with a single matrix.')
end
if isa(s,'cell')
  if (size(s,1) ~= 1)
    error('NNET:Arguments','Layer sizes is not a row vector of positive integers.')
  end
  for i=1:length(s)
    si = s{i};
    if ~isa(si,'double') || ~isreal(si) || any(size(si) ~= 1) || any(si<1) || any(round(si) ~= si)
      error('NNET:Arguments','Layer sizes is not a row vector of positive integers.')
    end
  end
  s = cell2mat(s);
end
if (~isa(s,'double')) || ~isreal(s) || (size(s,1) ~= 1) || any(s<1) || any(round(s) ~= s)
  error('NNET:Arguments','Layer sizes is not a row vector of positive integers.')
end

% Architecture

Nl = length(s)+1; %N-1 hidden + 1 output to decide the no of layers for the network
net = network(1,Nl); % 1 input N-1 HL 1 output layer network
net.biasConnect = ones(Nl,1);
net.inputConnect(1,1) = 1;
[j,i] = meshgrid(1:Nl,1:Nl);
net.layerConnect = (j == (i-1)) | ((j-1) == i); % here LC(1,2) = 1 and LC(2,1) = 1
net.outputConnect(Nl) = 1;

% Simulation
net.inputs{1}.exampleInput = p;
net.inputs{1}.processFcns = ipf; %pre-processing conditions for less arguments in the newjor

for i=1:Nl
    if (i<Nl)
        net.layerWeights{i,i+1}.delays = 1;
    else
       net.layerWeights{Nl,Nl}.delays = 1 ;
    end

  if (i<Nl), net.layers{i}.size = s(i); end
  if (length(tf) < i) || all(isnan(tf{i}))
    if (i<Nl)
      net.layers{i}.transferFcn = 'tansig';
    else
      net.layers{i}.transferFcn = 'purelin';
    end
  else
    net.layers{i}.transferFcn = tf{i};
  end
end
net.outputs{Nl}.exampleOutput = t;
net.outputs{Nl}.processFcns = tpf;

% Adaption
net.adaptfcn = 'trains';
net.inputWeights{1,1}.learnFcn = blf;
for i=1:Nl
  net.biases{i}.learnFcn = blf;
  net.layerWeights{i,:}.learnFcn = blf;
end

% Training
net.trainfcn = btf;
net.dividefcn = ddf;
net.performFcn = pf;
net.gradientFcn = feval(btf,'gdefaults',0);

% Initialization
net.initFcn = 'initlay';
for i=1:Nl
  net.layers{i}.initFcn = 'initnw';
end
net = init(net);

% Plots
net.plotFcns = {'plotperform','plottrainstate'};

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

%this is similar to the above function written for the lower end MATLAB
%version of NNT tooblox

function net = jordan_p0(pr,s,tf,btf,blf,pf)
% Backward compatible to NNT 5.0

if nargin < 1, error('NNET:Arguments','Not enough arguments.'),end

% Defaults
Nl = length(s);
if nargin < 3, tf = {'tansig'}; tf = tf(ones(1,Nl)); end
if nargin < 4, btf = 'traingdx'; end
if nargin < 5, blf = 'learngdm'; end
if nargin < 6, pf = 'mse'; end

% Error checking
if (~isa(pr,'double')) || ~isreal(pr) || (size(pr,2) ~= 2)
  error('NNET:Arguments','Input ranges is not a two column matrix.')
end
if any(pr(:,1) > pr(:,2))
  error('NNET:Arguments','Input ranges has values in the second column larger in the values in the same row of the first column.')
end
if isa(s,'cell')
  if (size(s,1) ~= 1)
    error('NNET:Arguments','Layer sizes is not a row vector of positive integers.')
  end
  for i=1:length(s)
    si = s{i};
    if ~isa(si,'double') || ~isreal(si) || any(size(si) ~= 1) || any(si<1) || any(round(si) ~= si)
      error('NNET:Arguments','Layer sizes is not a row vector of positive integers.')
    end
  end
  s = cell2mat(s);
end
if (~isa(s,'double')) || ~isreal(s) || (size(s,1) ~= 1) || any(s<1) || any(round(s) ~= s)
  error('NNET:Arguments','Layer sizes is not a row vector of positive integers.')
end

% Architecture
net = network(1,Nl);
net.biasConnect = ones(Nl,1);
net.inputConnect(1,1) = 1;
[j,i] = meshgrid(1:Nl,1:Nl);
net.layerConnect = (j == (i-1)) | ((j-1) == i);
net.outputConnect(Nl) = 1;

% Simulation
net.inputs{1}.range = pr;
for i=1:Nl
  if (i<Nl)
        net.layerWeights{i,i+1}.delays = [1];
    else
       net.layerWeights{Nl,Nl}.delays = [1] ;
    end
  net.layers{i}.size = s(i);
  net.layers{i}.transferFcn = tf{i};
end

% Performance
net.performFcn = pf;

% Adaption
net.adaptfcn = 'trains';
net.inputWeights{1,1}.learnFcn = blf;
for i=1:Nl
  net.biases{i}.learnFcn = blf;
  net.layerWeights{i,:}.learnFcn = blf;
end

% Training
net.trainfcn = btf;
net.gradientFcn = feval(btf,'gdefaults',0);

% Initialization
net.initFcn = 'initlay';
for i=1:Nl
  net.layers{i}.initFcn = 'initnw';
end
net = init(net);

% Plots
net.plotFcns = {'plotperform','plottrainstate'};


% mycorr2 modified version of the 2D correlation
%         for the use with im2col and col2im
%         see GETPOINT
%
%
% $Id: mycorr2.m,v 2.0 2003/06/19 12:07:11 svoboda Exp $

% Note: It written in order to gain speed. The clarity of the code suffers accordingly

function R = mycorr2(X,G,Gn,Gn2)

% Gn  = G-mean(G);
% Gn2 = sqrt(sum(Gn.^2));

mX	= repmat(mean(X),size(X,1),1);
mXn = X - mX;
smX	= sum(mXn.^2);

numerator = (mXn'*Gn)';
denominator = smX*Gn2;

R = numerator./denominator;

return


function [imdepthMin, imdepthMax, imdepthCol, contact, x, y, z] = makeDepthImages(bndinfo)

global DO_DISPLAY;
DO_DISPLAY = 0;

load '/home/dhoiem/src/cmu/iccv07Final/data/contactdt.mat';

[tmp, glabels] = max(bndinfo.result.geomProb, [], 2);
glabels((glabels>=2) & (glabels<=4)) = 2;
glabels(glabels==5) = 3;

lab = bndinfo.edges.boundaryType;

lab = lab(1:end/2) + 2*lab(end/2+1:end);

[imdepthMin, imdepthMax, imdepthCol, contact, x, y, z] = ...
    getDepthRangeForDisplay(bndinfo, glabels, lab, contactdt, 0.5);


% -------------------------------------------------------------------------------------------------
function [net, stats] = experiment_triplet(imdb_video, varargin)
%EXPERIMENT
%   main function - creates a network and trains it on the dataset indexed by imdb_video.
%
%   Luca Bertinetto, Jack Valmadre, Joao Henriques, 2016

% the enhancers are produced by the training net changed with each iteration
% delete pairwise loss.
% -------------------------------------------------------------------------------------------------
    % Default parameters (set the experiment-specific ones in run_experiment)
    opts.net.type = 'alexnet';
    opts.net.conf = struct(); % Options depend on type of net.
    opts.pretrain = false; % Location of model file set in env_paths.
    opts.init.scale = 1;
    opts.init.weightInitMethod = 'xavierimproved';
    opts.init.initBias = 0.1;
    opts.expDir = 'data'; % where to save the trained net
    opts.numFetchThreads = 12; % used by vl_imreadjpg when reading dataset
    opts.validation = 0.1; % fraction of imbd reserved to validation
    opts.exemplarSize = 127; % exemplar (z) in the paper
    opts.instanceSize = 255 - 2*8; % search region (x) in the paper
    opts.loss.type = 'simple';
    opts.loss.rPos = 16; % pixel with distance from center d > rPos are given a negative label
    opts.loss.rNeg = 0; % if rNeg != 0 pixels rPos < d < rNeg are given a neutral label
    opts.loss.labelWeight = 'balanced';
    opts.numPairs =  5.32e4; % Number of example pairs per epoch, if empty, then equal to number of videos.
    opts.randomSeed = 0;
    opts.shuffleDataset = false; % do not shuffle the data to get reproducible experiments
    opts.frameRange = 100; % range from the exemplar in which randomly pick the instance
    opts.gpus = [];
    opts.prefetch = false; % Both get_batch and cnn_train_dag depend on prefetch.
    opts.train.numEpochs = 50;
    opts.train.learningRate = logspace(-2, -5, opts.train.numEpochs);
    opts.train.weightDecay = 5e-4;
    opts.train.batchSize = 8; % we empirically observed that small batches work better
    opts.train.profile = false;
    % add parameter
%     opts.train.exemplarSize = opts.exemplarSize; % exemplar (z) in the paper
%     opts.train.instanceSize = opts.instanceSize; % search region (x) in the paper
%     opts.loss.weight  = 0.01;
    % Data augmentation settings
    opts.subMean = false;
    opts.colorRange = 255;
    opts.augment.translate = true;
    opts.augment.maxTranslate = 4;
    opts.augment.stretch = true;
    opts.augment.maxStretch = 0.05;
    opts.augment.color = true;
    opts.augment.grayscale = 0; % likelihood of using grayscale pair
    % Override default parameters if specified in run_experiment

    % Get environment-specific default paths.
    opts = env_paths_training(opts);
    opts.train.gpus = opts.gpus;
    opts.train.prefetch = opts.prefetch;

    opts = vl_argparse(opts, varargin);
% -------------------------------------------------------------------------------------------------
    % Get ImageNet Video metadata
    if isempty(imdb_video)
        fprintf('loading imdb video...\n');
        imdb_video = load(opts.imdbVideoPath);
        imdb_video = imdb_video.imdb_video;
    end

    % Load dataset statistics
    [rgbMean_z, rgbVariance_z, rgbMean_x, rgbVariance_x] = load_stats(opts);
    if opts.shuffleDataset
        s = RandStream.create('mt19937ar', 'Seed', 'shuffle');
        opts.randomSeed = s.Seed;
    end

    opts.train.expDir = opts.expDir;

    rng(opts.randomSeed); % Re-seed before calling make_net.

    % -------------------------------------------------------------------------------------------------
    net = make_net(opts);
    % -------------------------------------------------------------------------------------------------

    [imdb_video, imdb] = choose_val_set(imdb_video, opts);

    [resp_sz, resp_stride] = get_response_size(net, opts);
    % We want an odd number so that we can center the target in the middle
    assert(all(mod(resp_sz, 2) == 1), 'resp. size is not odd');

    [net, derOutputs, label_inputs_fn] = setup_loss(net, resp_sz, resp_stride, opts.loss);

%     enhancer = zeros(a_feat_sz(1),a_feat_sz(2),256,opts.train.batchSize,'single');
% %     imout_t(end)=1;
% %     if numel(opts.train.gpus) >= 1
% %         imout_t = gpuArray(imout_t);
% %     end
%     t_labels = ones(opts.train.batchSize,1);
%     neg_labels = -ones(opts.train.batchSize,1);
    batch_fn = @(db, batch) get_batch(db, batch, ...
                                        imdb_video, ...
                                        opts.rootDataDir, ...
                                        numel(opts.train.gpus) >= 1, ...
                                        struct('exemplarSize', opts.exemplarSize, ...
                                               'instanceSize', opts.instanceSize, ...
                                               'frameRange', opts.frameRange, ...
                                               'subMean', opts.subMean, ...
                                               'colorRange', opts.colorRange, ...
                                               'stats', struct('rgbMean_z', rgbMean_z, ...
                                                               'rgbVariance_z', rgbVariance_z, ...
                                                               'rgbMean_x', rgbMean_x, ...
                                                               'rgbVariance_x', rgbVariance_x), ...
                                               'augment', opts.augment, ...
                                               'prefetch', opts.train.prefetch, ...
                                               'numThreads', opts.numFetchThreads), ...
                                        label_inputs_fn);

    opts.train.derOutputs = derOutputs;
    opts.train.randomSeed = opts.randomSeed;
    % -------------------------------------------------------------------------------------------------
    % Start training
    [net, stats] = cnn_train_dag(net, imdb, batch_fn, opts.train);
    % -------------------------------------------------------------------------------------------------
end

% -----------------------------------------------------------------------------------------------------
function [rgbMean_z, rgbVariance_z, rgbMean_x, rgbVariance_x] = load_stats(opts)
% Dataset image statistics for data augmentation
% -----------------------------------------------------------------------------------------------------
    stats = load(opts.imageStatsPath);
    % Subtracted if opts.subMean is true
    if ~isfield(stats, 'z')
        rgbMean = reshape(stats.rgbMean, [1 1 3]);
        rgbMean_z = rgbMean;
        rgbMean_x = rgbMean;
        [v,d] = eig(stats.rgbCovariance);
        rgbVariance_z = 0.1*sqrt(d)*v';
        rgbVariance_x = 0.1*sqrt(d)*v';
    else
        rgbMean_z = reshape(stats.z.rgbMean, [1 1 3]);
        rgbMean_x = reshape(stats.x.rgbMean, [1 1 3]);
        % Set data augmentation statistics, used if opts.augment.color is true
        [v,d] = eig(stats.z.rgbCovariance);
        rgbVariance_z = 0.1*sqrt(d)*v';
        [v,d] = eig(stats.x.rgbCovariance);
        rgbVariance_x = 0.1*sqrt(d)*v';
    end
end

% -------------------------------------------------------------------------------------------------
function net = make_net(opts)
% -------------------------------------------------------------------------------------------------

    net = make_siam_adapt2(opts);
%     net = make_siameseFC(opts);

    % Save the net graph to disk.
    inputs = {'exemplar', [opts.exemplarSize*[1 1] 3 opts.train.batchSize], ...
              'instance', [opts.instanceSize*[1 1] 3 opts.train.batchSize]};
    net_dot = net.print(inputs, 'Format', 'dot');
    if ~exist(opts.expDir)
        mkdir(opts.expDir);
    end
    f = fopen(fullfile(opts.expDir, 'arch.dot'), 'w');
    fprintf(f, net_dot);
    fclose(f);
end

% -------------------------------------------------------------------------------------------------
function [resp_sz, resp_stride] = get_response_size(net, opts)
% -------------------------------------------------------------------------------------------------

    sizes = net.getVarSizes({'exemplar', [opts.exemplarSize*[1 1] 3 256], ...
                             'instance', [opts.instanceSize*[1 1] 3 256]});
    resp_sz = sizes{net.getVarIndex('score')}(1:2);
    rfs = net.getVarReceptiveFields('exemplar');
    resp_stride = rfs(net.getVarIndex('score')).stride(1);
    assert(all(rfs(net.getVarIndex('score')).stride == resp_stride));
end

% -------------------------------------------------------------------------------------------------
function [net, derOutputs, inputs_fn] = setup_loss(net, resp_sz, resp_stride, loss_opts)
% Add layers to the network, specifies the losses to minimise, and
% constructs a function that returns the inputs required by the loss layers.
% -------------------------------------------------------------------------------------------------
net.addLayer('tri_select', select_pairs(), ...
                 {'score', 'eltwise_label'}, ...
                 {'tri_pairs'}, ...
                 {});
    net.addLayer('objective_softmaxlog', ...
                 dagnn.Loss('loss', 'softmaxlog'), ...
                 {'tri_pairs','tri_pairs_label'}, 'objective_softmaxlog');
   net.vars(end).precious = 1;
%     %xcorr ac  correlation between exemplar and positive instance
%     net.addLayer('xcorr_ac', XCorr(), ...
%                  {'a_feat', 'c_feat'}, ...
%                  {'xcorr_ac_out'}, ...
%                  {});
%     add_adjust_layer(net, 'adjust_ac', 'xcorr_ac_out', 'score_ac', ...
%                  {'adjust_f', 'adjust_b'}, 1e-3, 0, 0, 1);
%     %xcorr ad   correlation between exemplar and negative instance
%     net.addLayer('xcorr_ad', XCorr(), ...
%                  {'a_feat', 'd_feat'}, ...
%                  {'xcorr_ad_out'}, ...
%                  {});
%     add_adjust_layer(net, 'adjust_ad', 'xcorr_ad_out', 'score_ad', ...
%                  {'adjust_f', 'adjust_b'}, 1e-3, 0, 0, 1);
%
% %     net.addLayer('concat', dagnn.Concat(), ...
% %                  {'score_ac', 'score_ad'}, ...
% %                  {'concat_out'}, ...
% %                  {});
%     % softmaxloss between score_ac and score_ad
%     net.addLayer('objective_softmax', ...
%                  TripletLoss2(), ...
%                  {'score_ac', 'score_ad'}, 'objective_softmax');
% %                  net.layers(end).block.opts = [...
% %         net.layers(end).block.opts, ...
% %         {'instanceWeights', loss_opts.weight}];
% %     add_adjust_layer(net, 'adjust_loss', 'objective_softmax', 'loss_softmax',  ...
% %                  {'adjust_f_sm', 'adjust_b_sm'}, 1e-3, 0, 1, 0);
%
% %     % create label and weights for logistic loss
% %     net.addLayer('objective', ...
% %                  dagnn.Loss('loss', 'logistic'), ...
% %                  {'score', 'eltwise_label'}, 'objective');
% %     % adding weights to loss layer

    [pos_eltwise, instanceWeight] = create_labels(...
        resp_sz, loss_opts.labelWeight, ...
        loss_opts.rPos/resp_stride, loss_opts.rNeg/resp_stride);
    neg_eltwise = [];   % no negative pairs at the moment

    ind_pos = pos_eltwise>0;
        ind_neg = pos_eltwise<0;
        ind_pos = ind_pos(:);
        ind_neg = ind_neg(:);
        n_pos = sum(ind_pos);
        n_neg = sum(ind_neg);
        ind_select = ones(n_pos,n_neg)>0;
        n_pairs = sum(ind_select(:));
        instanceWeight = ones(1,n_pairs,'single')/n_pairs;
    net.layers(end).block.opts = [...
        net.layers(end).block.opts, ...
        {'instanceWeights', instanceWeight}];

%         net.addLayer('sum_loss', ...
%                  dagnn.Sum('numInputs',2), ...
%                  {'objective', 'objective_softmax'}, 'sum_loss');

%         net.addLayer('sum_loss', ...
%                  weightSum('numInputs',2,'ws',[1,loss_opts.weight]), ...
%                  {'objective', 'objective_softmax'}, 'sum_loss');

%      add_norm_weight_sum_layer(net, 'sum_loss', {'objective', 'objective_softmax'}, {'sum_loss'}, ...
%                  {'ws'}, [0.9,0.1], 1);

%     % loss between a_feat and c_feat
%     net.addLayer('objective_ac', ...
%                  dagnn.Loss('loss', 'logistic'), ...
%                  {'score_ac', 'pos_label'}, 'objective_ac');
%     add_adjust_layer(net, 'adjust', 'score_ac', 'adjust_score_ac',  ...
%                  {'adjust_f_ac', 'adjust_b_ac'}, 1e-3, 0, 1, 0);
%     % loss between a_feat and d_feat
%     net.addLayer('objective_ad', ...
%                  dagnn.Loss('loss', 'logistic'), ...
%                  {'score_ad', 'neg_label'}, 'objective_ad');
%     add_adjust_layer(net, 'adjust', 'score_ad', 'adjust_score_ad',  ...
%                  {'adjust_f_ad', 'adjust_b_ad'}, 1e-3, 0, 1, 0);
%     net.layers(end).block.opts = [...
%         net.layers(end).block.opts, ...
%         {'instanceWeights', loss_opts.weight}];

%     derOutputs = {'sum_loss', 1};
%     derOutputs = {'objective', 1, 'objective_softmax',1};
    derOutputs = { 'objective_softmaxlog',1};

    inputs_fn = @(labels, obj_sz_z, obj_sz_x) get_label_inputs_simple(...
        labels, obj_sz_z, obj_sz_x, pos_eltwise, neg_eltwise, n_pairs);

    net.addLayer('errdisp', centerThrErr(), {'score', 'label'}, 'errdisp');
    net.addLayer('errmax', MaxScoreErr(), {'score', 'label'}, 'errmax');
end

% -------------------------------------------------------------------------------------------------
function inputs = get_label_inputs_simple(labels, obj_sz_z, obj_sz_x, pos_eltwise, neg_eltwise, n_pairs)
% GET_LABEL_INPUTS_SIMPME returns the network inputs that specify the labels.
%
% labels -- Label of +1 or -1 per image pair, size [1, n].
% obj_sz_z -- Size of exemplar box, dims [2, n].
% obj_sz_x -- Size of instance box, dims [2, n].
% -------------------------------------------------------------------------------------------------

%     pos = (labels > 0);
%     neg = (labels < 0);
%
%     resp_sz = size(pos_eltwise);
%     eltwise_labels = zeros([resp_sz, 1, numel(labels)], 'single');
%     eltwise_labels(:,:,:,pos) = repmat(pos_eltwise, [1 1 1 sum(pos)]);
%     eltwise_labels(:,:,:,neg) = repmat(neg_eltwise, [1 1 1 sum(neg)]);
        label0 = ones(1,n_pairs,1,numel(labels),'single');
    inputs = {'label', labels, ...
              'eltwise_label', pos_eltwise,...
              'tri_pairs_label',label0};
%           inputs = {'label', labels};
end

% -------------------------------------------------------------------------------------------------
function [imdb_video, imdb] = choose_val_set(imdb_video, opts)
% Designates some examples for validation.
% It modifies imdb_video and constructs a dummy imdb.
% -------------------------------------------------------------------------------------------------
    TRAIN_SET = 1;
    VAL_SET = 2;

    % set opts.validation to validation and the rest to training.
    size_dataset = numel(imdb_video.id);
    size_validation = round(opts.validation * size_dataset);
    size_training = size_dataset - size_validation;
    imdb_video.set = uint8(zeros(1, size_dataset));
    imdb_video.set(1:size_training) = TRAIN_SET;
    imdb_video.set(size_training+1:end) = VAL_SET;

    %% create imdb of indexes to imdb_video
    % train and val from disjoint video sets
    imdb = struct();
    imdb.images = struct(); % we keep the images struct for consistency with cnn_train_dag (MatConvNet)
    imdb.id = 1:opts.numPairs;
    n_pairs_train = round(opts.numPairs * (1-opts.validation));
    imdb.images.set = uint8(zeros(1, opts.numPairs)); % 1 -> train
    imdb.images.set(1:n_pairs_train) = TRAIN_SET;
    imdb.images.set(n_pairs_train+1:end) = VAL_SET;
end

% -------------------------------------------------------------------------------------------------
function inputs = get_batch(db, batch, imdb_video, data_dir, use_gpu, sample_opts, label_inputs_fn)
% Returns the inputs to the network.
% -------------------------------------------------------------------------------------------------

    [imout_z, imout_x, labels, sizes_z, sizes_x] = vid_get_random_batch(...
        db, imdb_video, batch, data_dir, sample_opts);
    if use_gpu
        imout_z = gpuArray(imout_z);
        imout_x = gpuArray(imout_x);
    end
    % Constructs full label inputs from output of vid_get_random_batch.
    label_inputs = label_inputs_fn(labels, sizes_z, sizes_x);
    inputs = [{'exemplar', imout_z, 'instance', imout_x}, label_inputs];
end


%SETFEATDOM Reset feature domains of dataset
%
%   A = SETFEATDOM(A,FEATDOM,K)
%
% FEATDOM has to be a cell-array, defining for each feature
% the desired domain. The following formats are supported:
% []         : empty, all numbers are allowed
% R size(N,2): Defines N ranges on the real axis,
%              lowerbounds are R(:,1), upperbounds R(:,2).
%              N SHOULD BE AT LEAST 2. E.g:
%              R = [0 1; 0 1] defines a single region between 0 and 1.
%              R = [-1 1; 5 10; 11 11] defines two regions and
%              a single value (11).
% J size(1,N): Set of N integer values
% S size(N,F): String array of N strings. The feature values in the
%              DATA field of A will be coded by integers from 1 to N.
%              A feature like this may be assigned to the dataset
%              by A(:,j) = S, in which S is a string array, containing
%              a string for each object.
% By this routine and also by all changes in the dataset, TESTFEATDOM
% will be called to check consistency between data and feature domain
% descriptions.
%
% If given, K has to be a set of indices with length equal to
% length(FEATDOM) pointing to the feature domains to be reset.
% Checking is done as well. So reset data first if needed.


% =========================================================================
% FUNCTION
% scd_scheme_display.m
%
% INPUT
% gradient_vectors			nx3
% OR scheme             nx9
%
% OUTPUT
% (-)
%
% EXAMPLE
%   scd_bvecs_display(scheme)
%
% EXAMPLE 2
%   scd_bvecs_display(scd_schemefile_read('qspace.scheme'))
%
% COMMENTS
% Julien Cohen-Adad 2009-10-02
% =========================================================================
%
% See also scd_schemefile_read
function scd_scheme_display(gradient_vectors,display_voronoi)

%% Check if gradient_vectors is a schemefile
if isstr(gradient_vectors)
    error('Input should be a matrix not a filename. Use scd_schemefile_read')
end

if size(gradient_vectors,2)>3
    gradient_vectors = gradient_vectors(:,1:3).*repmat(gradient_vectors(:,4),[1 3]);
end

% gradient_vectors=gradient_vectors/max(max(gradient_vectors));
gradient_norm = sqrt(gradient_vectors(:,1).^2+gradient_vectors(:,2).^2+gradient_vectors(:,3).^2);
display_3d = 0;
colorindex = jet(1000);
colorprct = max(1,round(gradient_norm/max(gradient_norm)*1000));
color = colorindex(colorprct,:);
% display gradients
if display_3d
	for i = 1:size(gradient_vectors,1)
		plot3(gradient_vectors(i,1),gradient_vectors(i,2),gradient_vectors(i,3),'.','MarkerSize',10,'Color',color(i,:))
		hold on
	end
	xlabel('X')
	ylabel('Y')
	zlabel('Z')
	axis vis3d;
	view(3), axis equal
	axis on, grid
	rotate3d on;
end

% display gradients
subplot(2,2,1)
for i = 1:size(gradient_vectors,1)
    plot3(gradient_vectors(i,1),gradient_vectors(i,2),gradient_vectors(i,3),'.','MarkerSize',10,'Color',color(i,:))
    hold on
end
xlabel('X')
ylabel('Y')
zlabel('Z')
axis vis3d;
view(3), axis equal
axis on, grid
rotate3d on;
view(0,0)
lim = max(max(abs([xlim; ylim; zlim])));
xlim([-lim lim]); ylim([-lim lim]); zlim([-lim lim]);

subplot(2,2,2)
for i = 1:size(gradient_vectors,1)
    plot3(gradient_vectors(i,1),gradient_vectors(i,2),gradient_vectors(i,3),'.','MarkerSize',10,'Color',color(i,:))
    hold on
end
xlabel('X')
ylabel('Y')
zlabel('Z')
axis vis3d;
view(3), axis equal
axis on, grid
rotate3d on;
view(90,0)
lim = max(max(abs([xlim; ylim; zlim])));
xlim([-lim lim]); ylim([-lim lim]); zlim([-lim lim]);

subplot(2,2,3)
for i = 1:size(gradient_vectors,1)
    plot3(gradient_vectors(i,1),gradient_vectors(i,2),gradient_vectors(i,3),'.','MarkerSize',10,'Color',color(i,:))
    hold on
end
xlabel('X')
ylabel('Y')
zlabel('Z')
axis vis3d;
view(3), axis equal
axis on, grid
rotate3d on;
view(0,90)
lim = max(max(abs([xlim; ylim; zlim])));
xlim([-lim lim]); ylim([-lim lim]); zlim([-lim lim]);

% % Voronoi transformation
if exist('display_voronoi','var') && display_voronoi
	X = gradient_vectors;
	h_fig = figure('name','Voronoi');
	[V,C] = voronoin(X);
	K = convhulln(X);
	d = [1 2 3 1];       % Index into K
	for i = 1:size(K,1)
	   j = K(i,d);
	   h(i) = patch(X(j,1),X(j,2),X(j,3),i,'FaceColor','white','FaceLighting','phong','EdgeColor','black');
	end
	hold off
	view(2)
	axis off
	axis equal
	colormap(gray);
	% title('One cell of a Voronoi diagram')
	axis vis3d;
	rotate3d on;
	print(h_fig,'-dpng',strcat(['fig_voronoi.png']));
end


function [str] = struct2assoc(s, varargin)
    % Converts a Matlab structure into a string that describes an
    % expression for Mathematica association
    %
    % @author ayonga @date 2016-11-23
    %
    % Copyright (c) 2016, AMBER Lab
    % All right reserved.
    %
    % Redistribution and use in source and binary forms, with or without
    % modification, are permitted only in compliance with the BSD 3-Clause
    % license, see
    % http://www.opensource.org/licenses/bsd-license.php

    if length(s) == 1 % a scalar structure
        fields = fieldnames(s);
        nfield = length(fields);

        field_strs = cell(nfield, 1);
        for i = 1:nfield
            field = fields{i};
            value = s.(field);
            value_str = general2math(value, varargin{:});
            %         if iscell(value)
            %             % Use double-braces to prevent a struct array from being created
            %             value_str = ['{', value_str, '}'];
            %         end
            field_strs{i} = [str2mathstr(field), '->', value_str];
        end

        str = ['<| ', ...
            implode(field_strs,', '), ...
            ' |>'];
    elseif length(s) > 1 % structure array
        expr_s = arrayfun(@(y)struct2assoc(y, varargin{:}),s,'UniformOutput',false);
        str = cell2tensor(expr_s,'ConvertString',false);
    end

end


function cnoise_test ( )

%*****************************************************************************80
%
%% CNOISE_TEST tests the CNOISE library.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    01 April 2011
%
%  Author:
%
%    John Burkardt
%
  timestamp ( );
  fprintf ( 1, '\n' );
  fprintf ( 1, 'CNOISE_TEST:\n' );
  fprintf ( 1, '  MATLAB version\n' );
  fprintf ( 1, '  Test the CNOISE library.\n' );

  m = 10000;
  n = 10000;
  var = 1.0;
  alpha = 1.0;
  cnoise_test01 ( m, n, var, alpha );

  m = 10000;
  n = 10000;
  var = 1.0;
  r = 1.0;
  alpha = 1.0;
  cnoise_test02 ( m, n, var, r, alpha );

  m = 10000;
  n = 10000;
  r = 1.0;
  alpha = 1.0;
  cnoise_test03 ( m, n, r, alpha );
%
%  Terminate.
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'CNOISE_TEST:\n' );
  fprintf ( 1, '  Normal end of execution.\n' );
  fprintf ( 1, '\n' );
  timestamp ( );

  return
end


function [rbm,dae,allZero,allOne] = ModelTraining(Mask,Dec,REAL)
% Training RBM and DAE

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

    %% Determine the size of hidden layers
    allZero = all(~Mask,1);
    allOne  = all(Mask,1);
    other   = ~allZero & ~allOne;
    K       = sum(mean(abs(Mask(:,other).*Dec(:,other))>1e-6,1)>rand(1,sum(other)));
    K       = min(max(K,1),size(Mask,1));

    %% Train RBM and DAE
    rbm = RBM(sum(other),K,10,1,0,0.5,0.1);
    rbm.train(Mask(:,other));
    if REAL
        dae = DAE(size(Dec,2),K,10,size(Dec,1),0.5,0.5,0.1);
        dae.train(Dec);
    else
        dae = [];
    end
end


function calpak_test33 ( )

%*****************************************************************************80
%
%% CALPAK_TEST33 tests MONTH_LENGTH_COMMON.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    25 March 2013
%
%  Author:
%
%    John Burkardt
%
  n_test = 4;

  fprintf ( 1, '\n' );
  fprintf ( 1, 'CALPAK_TEST33\n' );
  fprintf ( 1, '  For the Common calendar:\n' );
  fprintf ( 1, '  MONTH_LENGTH_COMMON returns month lengths.\n' );

  y_test(1) = 1582;
  y_test(2) = 1752;
  y_test(3) = 1900;
  y_test(4) = 2000;

  for i_test = 1 : n_test

    y = y_test(i_test);
    sy = y_to_s_common ( y );
    months = year_length_months_common ( y );
    days = year_length_common ( y );

    fprintf ( 1, '\n' );
    fprintf ( 1, '  %d\n',y );
    fprintf ( 1, '  %s\n', sy );
    fprintf ( 1, '    Year length in months = %d\n', months );
    fprintf ( 1, '    Year length in days = %d\n', days );
    fprintf ( 1, '\n' );

    for m = 1 : months
      month_name = month_to_month_name_common ( m );
      fprintf ( 1, '  %10s  %2d\n', month_name, month_length_common ( y, m ) );
    end

  end

  return
end


function obs_mask= ObservMask( band )
%MASK Create the mask of obervation using the overlap of all bands.
%
% Syntax
%
%     obs_mask= ObservMask( band )
%
% Description
%
%     Create the mask of obervation using the overlap of all bands, which
%     have been calculated in LoadData.
% History
%
%     1. Create this function. (22. September, 2017)
%
% Input arguments
%
%     band     Blue band.
%
% Output arguments
%
%     obs_mask      Observation mask(outside).
%
% See also: Loaddata
%
%
% Author:  Shi Qiu (shi.qiu@ttu.edu)
% Date: 22. September, 2017

    obs_mask=band>-9999;
%
%     obs_mask=band~=0;
%
%     %% simulate Sentinel 2 scene by 100 km x 100 km.
%     % spatial resolution is 30 m for each pixel. That means there will be
%     % 3333 x 3333 30m pixels should be selected expanding from the centre.
%     s2_pixels=[3333 3333];
%     s2_pixels_half=round(s2_pixels./2);
%     obs_dim=size(obs_mask);
%     pixel_centre=round(obs_dim./2);
%
%     s2_mask=obs_mask.*0;
%     r_start=pixel_centre(1)-s2_pixels_half(1);
%     r_end=pixel_centre(1)+s2_pixels_half(1);
%     c_start=pixel_centre(2)-s2_pixels_half(2);
%     c_end=pixel_centre(2)+s2_pixels_half(2);
%     s2_mask(r_start:r_end,c_start:c_end)=1;
%     obs_mask=s2_mask&obs_mask;
end