class SuggesterUtils {

    public static final int NORMALIZED_DOCUMENT_FREQUENCY_MULTIPLIER = 1000;

    private static final Logger logger = Logger.getLogger(SuggesterUtils.class.getName());

    private static final long DEFAULT_TERM_WEIGHT = 0;

    private SuggesterUtils() {
    }

    /**
     * Combines the suggestions from multiple suggesters with the same {@code phrase} and returns the
     * {@code resultSize} of the ones with the highest scores.
     * @param results suggestions
     * @param resultSize the size of the list to return
     * @return combined results from multiple suggesters
     */
    static List<LookupResultItem> combineResults(final List<LookupResultItem> results, final int resultSize) {
        LookupPriorityQueue queue = new LookupPriorityQueue(resultSize);

        Map<String, LookupResultItem> map = new HashMap<>();

        for (LookupResultItem item : results) {
            LookupResultItem storedItem = map.get(item.getPhrase());
            if (storedItem == null) {
                map.put(item.getPhrase(), item);
            } else {
                storedItem.combine(item);
            }
        }

        // `queue` holds only `resultSize` items with the highest score
        map.values().forEach(queue::insertWithOverflow);

        return queue.getResult();
    }

    /**
     * Computes score of the of the specified term.
     * @param indexReader reader where the term occurs
     * @param field term field
     * @param bytesRef term text
     * @return score for the term
     */
    static long computeScore(final IndexReader indexReader, final String field, final BytesRef bytesRef) {
        try {
            Term term = new Term(field, bytesRef);
            double normalizedDocumentFrequency = computeNormalizedDocumentFrequency(indexReader, term);

            return (long) (normalizedDocumentFrequency * NORMALIZED_DOCUMENT_FREQUENCY_MULTIPLIER);
        } catch (IOException e) {
            logger.log(Level.WARNING, e, () -> "Could not compute weight for " + bytesRef);
        }
        return DEFAULT_TERM_WEIGHT;
    }

    private static double computeNormalizedDocumentFrequency(final IndexReader indexReader, final Term term)
            throws IOException {
        int documentFrequency = indexReader.docFreq(term);

        return ((double) documentFrequency) / indexReader.numDocs();
    }

    /**
     * Decomposes the provided {@code query} into terms.
     * @param query query to decompose
     * @return terms that were in the {@code query}
     */
    public static List<Term> intoTerms(final Query query) {<FILL_FUNCTION_BODY>}

    /**
     * Decomposes the provided {@code query} into terms with the exception of {@link PhraseQuery}. Is useful when
     * determining which terms should not be suggested. {@link PhraseQuery} is exempted because not suggesting some
     * term which were contained in it is invalid.
     * @param query query to decompose
     * @return terms that were in the {@code query}
     */
    public static List<Term> intoTermsExceptPhraseQuery(final Query query) {
        if (query == null) {
            return Collections.emptyList();
        }

        List<Term> terms = new LinkedList<>();

        LinkedList<Query> queue = new LinkedList<>();
        queue.add(query);

        while (!queue.isEmpty()) {
            Query q = queue.poll();

            if (q instanceof BooleanQuery) {
                for (BooleanClause bc : ((BooleanQuery) q).clauses()) {
                    queue.add(bc.getQuery());
                }
            } else if (q instanceof TermQuery) {
                terms.add(((TermQuery) q).getTerm());
            }
        }

        return terms;
    }

    /**
     * Determines if the query is deemed complex by the suggester standards. Complex means that it needs to search in
     * the index rather than WFST data structure.
     * @param query dependent query
     * @param suggesterQuery suggester query
     * @return {@code true} if complex, {@code false} otherwise
     */
    public static boolean isComplexQuery(final Query query, final SuggesterQuery suggesterQuery) {
        return query != null || !(suggesterQuery instanceof SuggesterPrefixQuery);
    }

}

        if (query == null) {
            return Collections.emptyList();
        }

        List<Term> terms = new LinkedList<>();

        LinkedList<Query> queue = new LinkedList<>();
        queue.add(query);

        while (!queue.isEmpty()) {
            Query q = queue.poll();

            if (q instanceof BooleanQuery) {
                for (BooleanClause bc : ((BooleanQuery) q).clauses()) {
                    queue.add(bc.getQuery());
                }
            } else if (q instanceof TermQuery) {
                terms.add(((TermQuery) q).getTerm());
            } else if (q instanceof PhraseQuery) {
                terms.addAll(Arrays.asList(((PhraseQuery) q).getTerms()));
            }
        }

        return terms;
    
<no_super_class>