function [X, CriterionValue, Iteration] = bestPoint(this, varargin)
% BESTPOINT Return the best point found in a Bayesian
% optimization according to a criterion.
%
%   (1) X = bestPoint(RESULTS) returns the 1-row table X
%       representing the best feasible point according to the
%       default criterion.
%
%   (2) [X, CriterionValue] = bestPoint(RESULTS) also returns
%       the value of the specified criterion at X.
%
%   (3) [X, CriterionValue, Iteration] = bestPoint(RESULTS)
%       when Criterion is 'min-observed', 'min-visited-mean',
%       or 'min-visited-upper-confidence-interval', also
%       returns the iteration number at which the best point
%       occurred.
%
%   (4) [...] = bestPoint(RESULTS, Name, Value,...)
%       specifies additional name/value parameters:
%
%       Criterion   - Specifies the criterion used to choose
%                   the best point. Possible values are the
%                   following (case insensitive, hyphens are
%                   optional, and unique prefixes are
%                   recognized):
%           'min-observed'  - X is the feasible point at which
%                           the minimum observed Objective was
%                           found.
%           'min-mean'      - X is the feasible point at which
%                           the mean of the Objective function
%                           model is minimized.
%           'min-upper-confidence-interval'
%                           - X is the feasible point that
%                           minimizes an upper confidence
%                           interval according to the Objective
%                           function model. See the ALPHA
%                           parameter.
%           'min-visited-mean'
%                           - X is the feasible point at which
%                           the mean of the Objective function
%                           model is minimized, among those
%                           points at which the Objective
%                           function was evaluated.
%           'min-visited-upper-confidence-interval'
%                           - X is the feasible point that
%                           minimizes an upper confidence
%                           interval according to the Objective
%                           function model, among those points
%                           at which the Objective function was
%                           evaluated. See the ALPHA parameter.
%           Default: 'min-visited-upper-confidence-interval'
%
%       Alpha   - A probability value defining the upper
%               confidence interval when CRITERION is
%               'min-upper-confidence-interval' or
%               'min-visited-upper-confidence-interval'. The
%               upper confidence interval at X is the value Y
%               such that Prob(mean(ObjectiveFcn(X))>Y)=Alpha
%               according to the Objective function model.
%               Default: .01
%

%   Copyright 2016-2022 The MathWorks, Inc.

[varargin{:}] = convertStringsToChars(varargin{:});
C = bayesoptim.suppressWarnings();
% Parse Criterion and Alpha
[Criterion, Alpha] = internal.stats.parseArgs({'Criterion', 'Alpha'}, ...
    {'minvisitedupperconfidenceinterval', .01}, varargin{:});
% Parse Criterion value
Crit = bayesoptim.parseArgValue(Criterion, {'minobserved', ...
    'minmean', 'minupperconfidenceinterval', 'minvisitedmean',...
    'minvisitedupperconfidenceinterval'});
% Check Alpha
if ~(isscalar(Alpha) && isnumeric(Alpha) && isreal(Alpha) && Alpha>0 && Alpha<1)
    bayesoptim.err('BadAlpha');
end
% Apply criterion
Iteration = NaN;
switch Crit
    case {'minobserved'}
        [X, CriterionValue, Iteration] = minObservedPoint(this);
    case {'minmean'}
        [X, CriterionValue] = minMeanPoint(this);
    case {'minvisitedmean'}
        [X, CriterionValue, Iteration] = minVisitedMeanPoint(this);
    case {'minupperconfidenceinterval'}
        [X, CriterionValue] = minUCIPoint(this, Alpha);
    case {'minvisitedupperconfidenceinterval'}
        [X, CriterionValue, Iteration] = minVisitedUCIPoint(this, Alpha);
end
end
