classdef (Sealed) lsaModel
%

% Copyright 2017-2022 The MathWorks, Inc.

    properties (Dependent)
        NumComponents;
    end

    properties (Dependent,SetAccess=private)
        ComponentWeights;
        DocumentScores;
        WordScores;
        Vocabulary;
    end

    properties
        FeatureStrengthExponent;
    end

    properties (Access=private)
        NumComponents_I;
        MaxDim;
        U;
        V;
        SingularValues;
        Wordlist;
    end

    methods (Hidden,Static)
        function mdl=fromSVD(U,S,V,alpha,words)
            mdl = lsaModel;
            mdl.U = U;
            mdl.SingularValues = reshape(S,1,[]);
            mdl.V = V;
            mdl.FeatureStrengthExponent = alpha;
            mdl.MaxDim = numel(mdl.SingularValues);
            mdl.NumComponents_I = mdl.MaxDim;
            mdl.Wordlist = words;
        end
    end

    methods
        function v=transform(mdl, documents)
            if isa(documents,'bagOfWords')
                if ~isscalar(documents)
                    error(message('textanalytics:ldaModel:NonscalarBagOfWords'));
                end
                documents = setVocabulary(documents, mdl.Wordlist);
                documents = documents.Counts;
            end

            if isa(documents,'bagOfNgrams')
                if ~isscalar(documents)
                    error(message('textanalytics:ldaModel:NonscalarBagOfNgrams'));
                end

                if nnz(contains(mdl.Wordlist.Vocabulary,' ')) == 0
                    documents = setNgrams(documents, mdl.Wordlist); % same as bag of words
                else % contains n-grams, need to transform wordlist
                    [~,wl] = ...
                        textanalytics.internal.encodeNgramsWordlist(mdl.Wordlist,documents);
                    documents = setNgrams(documents, wl);
                end
                documents = documents.Counts;
            end

            if ~isnumeric(documents)
                textanalytics.internal.validateStringOrDocument(documents,2,nargin);
                if isa(documents,'tokenizedDocument') && ~iscolumn(documents)
                    error(message('textanalytics:ldaModel:TokenizedDocumentMustBeColumnVector'));
                end

                [documents,~] = textanalytics.internal.encodeNgramsWordlist(mdl.Wordlist,documents);
            end

            if size(documents,2) ~= size(mdl.U,1)
                error(message('textanalytics:LSA:TransformDataNumWordsMismatch', size(mdl.U,1)));
            end

            redMat = mdl.U(:,1:mdl.NumComponents);
            S_ = mdl.SingularValues(1:mdl.NumComponents);
            S_ = S_.^(mdl.FeatureStrengthExponent/2-1);
            redMat = redMat.*S_;
            v = full(documents*redMat);
        end

        function v=get.ComponentWeights(mdl)
            v = mdl.SingularValues.^2;
        end

        function d=get.NumComponents(mdl)
            d = mdl.NumComponents_I;
        end

        function mdl=set.NumComponents(mdl,d)
            validateattributes(d, {'numeric'}, {'scalar','integer','positive'});
            if d > mdl.MaxDim
                error(message('textanalytics:LSA:MaxDim',mdl.MaxDim));
            end
            mdl.NumComponents_I = d;
        end

        function v=get.WordScores(mdl)
            U_ = mdl.U(:,1:mdl.NumComponents);
            S_ = mdl.SingularValues(1:mdl.NumComponents);
            S_ = S_.^(mdl.FeatureStrengthExponent/2);
            v = S_.*U_;
        end

        function v=get.DocumentScores(mdl)
            V_ = mdl.V(1:mdl.NumComponents,:);
            S_ = mdl.SingularValues(1:mdl.NumComponents);
            S_ = S_.^(mdl.FeatureStrengthExponent/2);
            v = (S_(:).*V_).';
        end

        function s = get.Vocabulary(mdl)
            wl = mdl.Wordlist;
            s = wl.Vocabulary;
        end
    end

    methods(Access = private)
        function mdl=lsaModel
        end
    end
end
