function X = internal_embed(X,weights,errorOnOOV) 
%INTERNAL_EMBED Extract embedding from matrix WEIGHTS using indices in X
%
%    Y = INTERNAL_EMBED(X,WEIGHTS,EMBEDSIZE,ERRORONOOV) returns the
%    embedding vectors in WEIGHTS corresponding to the numeric indices
%    in X. If ERRORONOOV is true, then WEIGHTS is a matrix of size 
%    EMBEDSIZE-by-VocabularySize and it will error for X>VocabularySize or
%    X==0 or X==nan. For now, this flag can only be activated via
%    wordEmbeddingLayer. If ERRORONOOV is false (default), WEIGHTS is a 
%    matrix of size EMBEDSIZE-by-(VocabularySize+1). Each element of X that
%    is not in the set 1:VocabularySize is replaced by the index
%    VocabularySize+1. Y has shape EMBEDSIZE-by-size(X).

%   Copyright 2020-2023 The MathWorks, Inc.

if ~isreal(X) || ~isreal(weights)
    error(message('deep:dlarray:ComplexNotSupported'))
end

% Get vocabulary dimension (includes OOV word if errorOnOOV==false)
[~, vocabSize] = size(weights);

if errorOnOOV
    if any(X>vocabSize | X==0 | isnan(X), 'all')
        error(message("deep:dlarray:IndicesMustBeInVocab", vocabSize));
    end
else
    % Accounts for out-of-vocabulary tokens. Tokens not present in the
    % vocabulary of 1-to-VocabSize correspond to the OOV-th column of
    % weights. NaNs are mapped to vocabSizeAndOOV.
    X(isnan(X)) = vocabSize;
    X(X == 0 | X > vocabSize) = vocabSize;
end

if isa(X, "gpuArray") && ~isa(weights, "gpuArray")
    weights = gpuArray(weights);
end

try
    X = reshape(weights(:,X), [size(weights,1) size(X)]); 
catch e
    if e.identifier == "MATLAB:badsubscript"
        error(message('deep:dlarray:EmbedNonValidIndices'))
    else
        throw(e)
    end
end
