classdef SimulationHistogramBasedStrategy < fxptds.RangeAggregation.SimpleExtractionStrategy
    %% SIMULATIONHISTOGRAMBASEDSTRATEGY class
    % This class extracts simulation ranges from a group. Internally the
    % class calls the simple extraction for the simulation ranges. If any
    % of the ranges are infinite, then we try to salvage the ranges using
    % histogram information. If no histogram information is available, then
    % we return empty
    
    % Copyright 2018 The MathWorks, Inc.
    
    properties(Constant, Hidden)
        BinIndex = 1; % bin (powers of 2) column index in the bin data structure
        PositiveBinIndex = 2; % positive column index in the bin data structure
        NegativeBinIndex = 3; % negative column index in the bin data structure
    end
    
    methods
        function this = SimulationHistogramBasedStrategy()
            % call the parent class to register the simulation range type
            this@fxptds.RangeAggregation.SimpleExtractionStrategy(fxptds.RangeType.Simulation);
        end
        
        function range = extractRange(this, group)
            % get the range with simple extraction
            range = extractRange@fxptds.RangeAggregation.SimpleExtractionStrategy(this, group);
            
            % if any range is infinite, try to extract the range based on
            % histogram information
            if any(isinf(range))
                % get the group members
                members = group.members.values;
                mergedHistogramData = members{1}.HistogramData;
                for mIndex = 2:numel(members)
                    mergedHistogramData = fxptds.HistogramUtil.mergeHistogramData(mergedHistogramData, members{mIndex}.HistogramData);
                end
                
                range = []; % initialize to empty; if no bins stays empty
                if ~isempty(mergedHistogramData.BinData)
                    % extract information for the positive bin
                    maxPositiveBin = this.getMaxBin(mergedHistogramData.BinData, this.PositiveBinIndex);
                    
                    % extract information for the negative bin
                    maxNegativeBin = this.getMaxBin(mergedHistogramData.BinData, this.NegativeBinIndex);
                    
                    % recreate range information based on bin values
                    range = [-this.getValue(maxNegativeBin), this.getValue(maxPositiveBin)];
                end
            end
        end
        
    end
    
    methods(Hidden)
        function value = getValue(~, bin)
            % recreate the value of a bin using the floor heuristic used in
            % instrumentation
            value = 0; % initialize to zero; if no bin information, stays zero
            if ~isempty(bin)
                value = 2^(bin+1);
            end
            
        end
        
        
        function maxBin = getMaxBin(~, binData, index)
            % find the maximum bin from the histogram information
            % NOTE: we need the maximum non-zero bin; hence the return
            % value may be empty which is handled in the caller site
            maxBin = double(max(binData(binData(:,index)>0,1)));
            
        end
        
    end
end

