function [X, bo] = choosePointASHA(bo)
% Choose a point using the ASHA algorithm. Either an exising
% point is promoted to the next rung, or a random XFeasible
% point is added to rung 1.
%

%   Copyright 2016-2022 The MathWorks, Inc.

NumRungs = bo.PrivOptions.NumTrainingSetSizes;
Max   = bo.PrivOptions.MaxTrainingSetSize;
eta = bo.PrivOptions.TrainingSetSizeMultiplier;
% Promote an existing point if possible
if bo.NumObjectiveEvaluations > 0
    for rung = NumRungs-1 : -1 : 1
        % Find out how many points should have been promoted
        % from this rung by now, and how many have been
        % promoted. Promote a point if necessary.
        isFiniteInRung = bo.ASHARungNum==rung & isfinite(bo.ObjectiveTrace(:)');
        numFiniteInRung = sum(isFiniteInRung);
        numShouldBePromotedInRung = floor(numFiniteInRung/bo.PrivOptions.TrainingSetSizeMultiplier);
        numPromotedInRung = sum(isFiniteInRung & bo.ASHAIsPromoted);
        if numShouldBePromotedInRung > numPromotedInRung
            % Find the best unpromoted point in the rung
            idxUnpromotedInRung = find(isFiniteInRung & ~bo.ASHAIsPromoted);
            [~,mini] = min(bo.ObjectiveTrace(idxUnpromotedInRung));
            idxToPromote = idxUnpromotedInRung(mini);
            % Promote the point
            X       = bo.XTrain(idxToPromote,:);
            newRung = rung+1;
            XTable  = untransformPoints(bo, X, true);
            XTable.ASHARung = newRung;
            XTable.TrainingSetSize = ceil(Max*eta^(newRung-NumRungs));
            X = transformPoints(bo, XTable);
            % Mark the point as promoted
            bo.ASHAIsPromoted(idxToPromote) = true;
            return;
        end
    end
end
% There is no promotable config, so add a new random point to rung 1
X = randomASHAFeasiblePoints(bo, 1);
end
