function [hh, ax] = pareto(varargin)
    %PARETO Pareto chart.
    %   PARETO(Y) produces a Pareto chart where the values in the vector Y
    %   are drawn as bars in descending order.  Each bar will be labeled 
    %   with with its element index in Y.
    %
    %   PARETO(Y,NAMES) labels each bar with the associated text from the  
    %   cell array or string vector NAMES.
    %
    %   PARETO(Y,X) labels each bar of Y with the associated value from X.
    %
    %   PARETO(...,THRESHOLD) Specify the amount of the cumulative
    %   distribution displayed as a proportion between 0 and 1. The default
    %   value for THRESHOLD is .95.
    %
    %   PARETO(AX,...) plots into AX as the main axes, instead of GCA.
    %
    %   H = PARETO(...) returns the primitive Line and Bar objects created.
    %
    %   [H,AX] = PARETO(...) additionally returns the two axes objects 
    %   created.
    %
    %   See also HISTOGRAM, BAR.
    
    %   Copyright 1984-2023 The MathWorks, Inc.

    % Parse possible Axes input
    [cax, args, nargs] = axescheck(varargin{:});
    
    if nargs == 0
        error(message('MATLAB:pareto:NotEnoughInputs'));
    end
    y = args{1};
    y = matlab.graphics.chart.internal.datachk(y,'numeric');
    
    cutoff = .95;
    names = compose("%d",1:numel(y));
    
    if nargs == 2 
        if isscalar(args{2})
            cutoff = args{2};
            names = compose("%d",1:numel(y));
        else
            names = args{2};
            names=string(names);
        end
    elseif nargs == 3
        names = string(args{2});
        cutoff = args{3};
    end
    
    if ~isvector(y) || isscalar(y)
        error(message('MATLAB:pareto:YMustBeVector'));
    end
    
    % If the data are complex, disregard any complex component, and warn the user.
    if (~isreal(y))
        warning(message('MATLAB:specgraph:private:specgraph:UsingOnlyRealComponentOfComplexData'));
        y = real(y);
    end
    
    if ~isnumeric(cutoff) || cutoff<0 || cutoff>1
        error(message('MATLAB:pareto:InvalidThreshold'))
    end
    
    y = y(:);
    [yy, ndx] = sort(y);
    yy = flipud(yy);
    ndx = flipud(ndx);
    
    cax = newplot(cax);
    parent = cax.Parent;
    fig = ancestor(cax, 'figure');

    % Error if AutoResizeChildren is 'on'
    if isprop(parent,'AutoResizeChildren') && strcmp(parent.AutoResizeChildren,'on')
        error(message('MATLAB:pareto:AutoResizeChildren'))
    end    
    
    hold_state = ishold(cax);
    
    h = bar(cax, 1:length(y), yy);
    
    h = [h; line(1:length(y), cumsum(yy), 'Parent', cax)];
    ysum = sum(yy);
    
    if ysum == 0
        ysum = eps;
    end
    k = min(find(cumsum(yy) / ysum > cutoff, 1), 10);
    
    if isempty(k)
        k = min(length(y), 10);
    end
    
    xLim = [.5 k+.5];
    yLim = [0 ysum];
    set(cax, 'XLim', xLim);
    set(cax, 'XTick', 1:k, 'XTickLabel', names(ndx(1:k)), 'YLim', yLim);
    
    % Hittest should be off for the transparent axes so that click and
    % mouse motion events are attributed to the opaque axes.
    raxis = axes('Color', 'none', 'XGrid', 'off', 'YGrid', 'off', ...
        'YAxisLocation', 'right', 'XLim', xLim, 'YLim', yLim, ...
        'HitTest', 'off', 'HandleVisibility', get(cax, 'HandleVisibility'), ...
        'Parent', parent);
    resizePareto(cax, raxis)
    
    yticks = get(cax, 'YTick');
    if max(yticks) < .9 * ysum
        yticks = unique([yticks, ysum]);
    end
    set(cax, 'YTick', yticks)
    yticklabels=compose("%0.0f%%",100*yticks/ysum);
    set(raxis, 'YTick', yticks, 'YTickLabel', yticklabels, 'XTick', []);
    set(fig, 'CurrentAxes', cax);
    linkaxes([raxis, cax],'xy');
    addlistener(cax, 'MarkedClean', @(~,~) resizePareto(cax, raxis));
    
    if ~isa(parent,'matlab.graphics.layout.Layout')
        raxis.PositionConstraint = 'innerposition';
        if ~hold_state
            hold(cax, 'off');
            set(fig, 'NextPlot', 'replacechildren');
        end
    end

    % Add MetaDataSevice flag
    if ~isprop(cax, 'FDT_Accessor')
        prop = addprop(cax, 'FDT_Accessor');
        prop.Transient = true;
        prop.Hidden = true;
    end
    
    cax.FDT_Accessor = 'pareto';
    
    if nargout > 0
        hh = h;
        ax = [cax raxis];
    end
end

function resizePareto(cax, raxis)
if isvalid(raxis) && ( ...
        ~isequal(raxis.Layout, cax.Layout) || ...
        ~isequal(raxis.Units, cax.Units) || ...
        ~isequal(raxis.InnerPosition, cax.InnerPosition))
    raxis.Layout = cax.Layout;
    if isempty(cax.Layout)
        raxis.Units = cax.Units;
        raxis.InnerPosition = cax.InnerPosition;
    end
end
end
