function opts = parforOptions(executor, varargin)
% opts = parforOptions(clus) builds a set of parfor options structure to be used with
% parfor to specify that the body of the parfor loop should be executed directly
% on workers in parallel.Cluster clus without creating a parallel pool. Instead,
% independent tasks will be submitted to the cluster to execute the body of the
% loop.
%
% When building parforOptions with a parallel.Cluster, it is important that the
% NumWorkers property of the cluster accurately reflects the number of workers
% available, otherwise the loop will not be partitioned as expected.
%
% opts = parforOptions(pool) builds a set of parfor options to be used with
% parfor to specify that the body of the parfor loop should be executing using
% the parallel.Pool instance pool.
%
% opts = parforOptions(..., 'RangePartitionMethod', method) defines how the
% iterations of the parfor loop will be divided into sub-ranges. Each "sub-range" is
% a contiguous block of loop iterations that will be executed as a group on a
% worker. Controlling the range partitioning can enable the performance of a
% parfor loop to be optimized. Each sub-range should be large enough that the
% overhead of scheduling the sub-range is small compared with the execution time
% of that sub-range. Sub-ranges should be small enough that the loop iterates can
% be divided into a sufficient number of sub-ranges to keep all the workers busy.
%
% Valid values for the parameter 'RangePartitionMethod' are:
%
%   'auto' - the default sub-range partitioning method. This divides the
%   loop into sub-ranges of varying sizes to try and achieve good performance for
%   a variety of parfor loops. In the special case where the number of loop
%   iterations is equal to the number of workers NW available, then the 'auto'
%   method divides the loop into NW sub-ranges of size 1.
%
%   'fixed' - use fixed sub-ranges sizes. You must also specify SubrangeSize
%   to choose the size of the subranges.
%
%   @customFcn - uses function customFcn to divide the loop iterations into
%   sub-ranges. customFcn is invoked as SZS = customFcn(N, NW) where N is the
%   number of iterations in the parfor loop, and NW is the number of workers
%   available to execute the loop. When the loop is running on a parallel
%   pool, NW is the size of the pool. When the loop is running using a
%   cluster, NW is the NumWorkers property of the cluster. customFcn returns a
%   vector SZS of sub-range sizes. It is required that sum(SZS) == N.
%
% opts = parforOptions(..., 'RangePartitionMethod', 'fixed', 'SubrangeSize', N) divides
% the loop iterations into sub-ranges of size approximately N.
%
% 'SubrangeSize' is only supported when 'RangePartitionMethod' is 'fixed'.
%
%
% opts = parforOptions(clus, ..., 'PARAM1', VAL1, 'PARAM2', VAL2, ...) can
% specify the following additional properties for a parfor options set for use
% with parallel.Cluster clus:
%
%
% 'AutoAddClientPath' - A logical scalar controlling whether user-added-entries on
%                       the client's path are added to each worker's path. The
%                       default is true.
% 'AutoAttachFiles'   - A logical scalar to choose whether to perform automatic
%                       dependency analysis. The default is true.
% 'AttachedFiles'     - A character vector, string, string array, or cell array of
%                       character vectors to specify files to transfer to the workers.
%                       The default is {}.
% 'AdditionalPaths'   - A character vector, string, string array, or cell array of
%                       character vectors that defines paths to be added to the
%                       workers' MATLAB path before the script or function is executed.
%
% See also parfor, parcluster, parpool.

% Copyright 2018-2019 The MathWorks, Inc.

narginchk(1, Inf);

if isscalar(executor) && isa(executor, 'parallel.Pool')
    % Short-circuit for the commonest case
    opts = parallel.parfor.PoolOptions.build(executor, varargin{:});
    return
end
    
validateattributes(executor, {'parallel.Pool', 'parallel.Cluster'}, ...
                   {'scalar'}, mfilename, 'executor', 1);

try
    if isa(executor, 'parallel.Pool')
        opts = parallel.parfor.PoolOptions.build(executor, varargin{:});
    elseif isa(executor, 'parallel.Cluster')
        opts = parallel.parfor.ClusterOptions.build(executor, varargin{:});
    end
catch E
    rethrow(E);
end
end
