%HashKeyPartitioner
% A mapping from keys to reduce task buckets that uses MD5 hash to achieve
% a likely uniform random distribution over the buckets.

%   Copyright 2014-2022 The MathWorks, Inc.

classdef (Sealed) HashKeyPartitioner < handle
    properties (GetAccess = private, SetAccess = immutable)
        % The number of reduce task buckets.
        NumBuckets;
    end

    properties (Access = private, Constant)
        % For generating a MD5 hash of the bytes of a key
        MD5Digester = parallel.internal.crypto.Digester(parallel.internal.crypto.DigestAlgorithm.MD5)

        % For encoding a string key as bytes
        StringEncoder = parallel.internal.crypto.StringEncoder(parallel.internal.crypto.StringEncoding.UTF16LE)
    end

    methods
        function this = HashKeyPartitioner(numBuckets)
            assert(isnumeric(numBuckets) && isscalar(numBuckets));
            this.NumBuckets = numBuckets;
        end

        % Map the given keys to the corresponding reduce task buckets.
        function bucketIndices = partition(obj, keys)
            if isnumeric(keys)
                bucketIndices = parallel.internal.crypto.partitionByDigest(keys, obj.NumBuckets, obj.MD5Digester);
            elseif iscellstr(keys) || isstring(keys)
                bucketIndices = parallel.internal.crypto.partitionByDigest(keys, obj.NumBuckets, obj.MD5Digester, obj.StringEncoder);
            else
                assert(false, 'Invalid key type passed to HashKeyPartitioner.');
            end
        end
    end

end
