    
function  u = processMetric(rootId, metric, collectedMetricData, opFcn, opChar)
%
%   
% Typical case: collectedMetricData = [lhs, rhs];

%   Copyright 2019 The MathWorks, Inc.

try    
    if isempty(collectedMetricData)
        u = [];
        return;
    end
    
    metricEnumVal = cvi.MetricRegistry.getEnum(metric); 
    checkVarSize = true;   
    
    % MCDC and sigrange/sigsize are special-cases.
    if( strcmp(metric,'mcdc'))
        u = process_metric_mcdc(rootId, collectedMetricData, opChar, metricEnumVal);
    elseif strcmpi(metric,'sigrange') || strcmpi(metric,'sigsize')
        u = process_metric_sig(collectedMetricData, opChar);
        checkVarSize = false;
    else % Generic case
        u = opFcn(collectedMetricData);
    end

    if checkVarSize
        u = adjust_variable_size_data(rootId, metricEnumVal, u, collectedMetricData); 
    end

catch MEx
    rethrow(MEx);
end
end

function u = process_metric_mcdc(rootId, collectedMetricData, opChar, metricEnumVal)
    firstTest = cv('get', rootId, '.firstTest');
    mcdcMode = cv('get', firstTest, '.mcdcMode');

    % Combine predSatisfied (valid for Masking mode) and 
    % pathHitBits (used to recalculate predSatisfied when not in
    % Masking mode)
    u = collectedMetricData(:,1);
    for i=2:size(collectedMetricData,2)
        lhs = u;
        rhs = collectedMetricData(:,i);
        u = cv('BitOp', lhs, opChar, rhs);

        % If in Masking MCDC mode, determine correct trueTableEntry and
        % falseTableEntry data for each predicate in each mcdc object.
        if (mcdcMode == SlCov.McdcMode.Masking)


            % Find all mcdcentry objects under specified root.
            cvId = cv('get',rootId,'.topSlsf');
            allSlsfObjs = cv('DecendentsOf',cvId);
            mcdcObjs = cv('MetricGet', allSlsfObjs, metricEnumVal,'.baseObjs');
            mcdcObjs(mcdcObjs == 0) = [];

            for mcdcIdx=1:length(mcdcObjs)
                [numPredicates, predSatisfied, trueTableEntry, falseTableEntry] = ...
                    cv('get', mcdcObjs(mcdcIdx), '.numPredicates', ...
                                                 '.dataBaseIdx.predSatisfied', ...
                                                 '.dataBaseIdx.trueTableEntry', ...
                                                 '.dataBaseIdx.falseTableEntry');
                
                for predIdx = 1:numPredicates
                    predSatIdx = predSatisfied + predIdx; % Index into predSatisfied section of data
                    trueIdx = trueTableEntry + predIdx;   % Index into trueTableEntry section of data
                    falseIdx = falseTableEntry + predIdx; % Index into falseTableEntry section of data


                    % For plus, use the true/false outcome from the side that satisfied that outcome.
                    % (Default to using rhs outcome if outcome is unsatisfied.)
                    % For all other operations, always use lhs outcome.
                    if (opChar ~= '+') || (lhs(predSatIdx) == SlCov.PredSatisfied.True_Only) || (lhs(predSatIdx) == SlCov.PredSatisfied.Fully_Satisfied)
                        u(trueIdx) = lhs(trueIdx);
                    else %if (rhs(predSatIdx) == SlCov.PredSatisfied.True_Only) || (rhs(predSatIdx) == SlCov.PredSatisfied.Fully_Satisfied)
                        u(trueIdx) = rhs(trueIdx);
                    end
                    if (opChar ~= '+') || (lhs(predSatIdx) == SlCov.PredSatisfied.False_Only) || (lhs(predSatIdx) == SlCov.PredSatisfied.Fully_Satisfied)
                        u(falseIdx) = lhs(falseIdx);
                    else %if (rhs(predSatIdx) == SlCov.PredSatisfied.False_Only) || (rhs(predSatIdx) == SlCov.PredSatisfied.Fully_Satisfied)
                        u(falseIdx) = rhs(falseIdx);
                    end 
                    
                    if(opChar == '-')
                        % Correction needed for subtraction.
                        u(predSatIdx) = bitor(u(predSatIdx), 1);
                    end
                    
                end
            end
        end
    end
end
        
function u = process_metric_sig(collectedMetricData, opChar)
    [numRows, numCols] = size(collectedMetricData);
    minIdxs = 1:2:(numRows-1);
    maxIdxs = 2:2:numRows;
    minRows = collectedMetricData(minIdxs, :);
    maxRows = collectedMetricData(maxIdxs, :);
    u = zeros(numRows,1);
    switch opChar
        case '+'
            u(minIdxs) = min(minRows, [], 2);  % min 
            u(maxIdxs) = max(maxRows, [], 2);  % max

        case '*'
            minout = max(minRows, [], 2); % min
            maxout = min(maxRows, [], 2); % max
            u(minIdxs) = minout; % min
            u(maxIdxs) = maxout; % max
            infIdx = find(maxout<minout);
            if ~isempty(infIdx)
                u(2*infIdx - 1) = inf; % min
                u(2*infIdx) = -inf; % max
            end

        case '-'
            % This operation is not completely well defined because we
            % don't distinguish between open and closed intervals.  
            %
            % Base the result on the following axioms:
            % range - point = range
            % point - range = empty if range overlaps, pt otherwise.
            % range - range = range difference
            % empty - anything = empty
            %

            % Assume just two operands
            minlhs = minRows(:,1);
            maxlhs = maxRows(:,1);
            minrhs = minRows(:,2);
            maxrhs = maxRows(:,2);

            empty_lhs = maxlhs<minlhs;

            emptyIdx = empty_lhs | (~empty_lhs & minrhs<=minlhs & maxrhs>=maxlhs);

            inter_min = max([minlhs minrhs]')'; %#ok<UDIM> % min
            inter_max = min([maxlhs maxrhs]')'; %#ok<UDIM> % max

            hasMinOverlap = inter_min==minlhs;
            hasMaxOverlap = inter_max==maxlhs;


            minout = inter_max;
            minout(~hasMinOverlap) = minlhs(~hasMinOverlap);
            maxout = inter_min;
            maxout(~hasMaxOverlap) = maxlhs(~hasMaxOverlap);

            minout(emptyIdx) = inf;
            maxout(emptyIdx) = -inf;

            u(minIdxs) = minout; % min
            u(maxIdxs) = maxout; % max
    end
end

%================================    
 function u = adjust_variable_size_data(rootId, metricEnumVal, u, trace)

     cvId = cv('get',rootId,'.topSlsf');
    if cv('MetricGet', cvId , metricEnumVal, '.hasVariableSize')
        allSlsfObjs = [cv('DecendentsOf',cvId) cvId];        
        [metricObjs, varShallowIdxs, varDeepIdxs] = cv('MetricGet', allSlsfObjs, metricEnumVal,'.baseObjs',...
                '.dataCnt.varShallowIdx','.dataCnt.varDeepIdx');
        u  = get_max_idx(varShallowIdxs, u, trace);            
        u  = get_max_idx(varDeepIdxs, u, trace);            

        switch (metricEnumVal)
            case cvi.MetricRegistry.getEnum('decision') 
                fieldTxt = '.dc.activeOutcomeIdx';
            case cvi.MetricRegistry.getEnum('condition')
                fieldTxt = '.coverage.activeCondIdx';
            case cvi.MetricRegistry.getEnum('mcdc')
                fieldTxt = '.dataBaseIdx.activeCondIdx';
            otherwise
                fieldTxt = '';
        end
        if ~isempty(fieldTxt)
            u =  fix_metrics(metricObjs, u, trace, fieldTxt);
        end
    end
 end
%================================    
 function u =  fix_metrics(metricObjs, u, trace, fieldTxt)    
    activeCondIdx = cv('get',metricObjs, fieldTxt)';
    u = get_max_idx(activeCondIdx, u, trace);
 end
%================================
function u = get_max_idx(activeIdx, u, trace)
    activeIdx(activeIdx < 0) = [];
    if ~isempty(activeIdx)
        activeIdx = activeIdx + 1;
        u(activeIdx) = max(trace(activeIdx,:),[],2);
    end
end