class PostingsAndPosition {
        private final PostingsEnum postings;
        private final int offset;
        private int freq;
        private int upTo;
        private int pos;

        PostingsAndPosition(PostingsEnum postings, int offset) {
            this.postings = postings;
            this.offset = offset;
        }
    }

    // custom begins – only necessary attributes
    private final Map<Integer, IntsHolder> documentToPositionsMap = new HashMap<>();

    private final int offset;

    private final DocIdSetIterator conjunction;
    private final PostingsAndPosition[] postings;
    // custom ends

    // custom – constructor parameters
    /**
     * Creates custom exact phrase scorer which remembers the positions of the found matches.
     * @param weight query weight
     * @param postings postings of the terms
     * @param offset the offset that is added to the found match position
     */
    CustomExactPhraseScorer(
            final Weight weight,
            final CustomPhraseQuery.PostingsAndFreq[] postings,
            final int offset
    ) {
        super(weight);

        this.offset = offset; // custom

        // custom begins – support for single term
        conjunction = generateDocIdSetIterator(Arrays.stream(postings)
                .map(p -> p.postings).collect(Collectors.toList()));
        // custom ends
        assert TwoPhaseIterator.unwrap(conjunction) == null;
        this.postings = Arrays.stream(postings)
                .map(p -> new PostingsAndPosition(p.postings, p.position))
                .toArray(PostingsAndPosition[]::new);
    }

    private DocIdSetIterator generateDocIdSetIterator(@NotNull List<DocIdSetIterator> listOfDocIdSetIterator) {
        return Optional.of(listOfDocIdSetIterator)
                .filter(p -> p.size() > 1)
                .map(ConjunctionUtils::intersectIterators)
                .orElse(listOfDocIdSetIterator.get(0));

    }

    @Override
    public TwoPhaseIterator twoPhaseIterator() {
        return new TwoPhaseIterator(conjunction) {
            @Override
            public boolean matches() throws IOException {
                // custom – interrupt handler
                if (Thread.currentThread().isInterrupted()) {
                    throw new IOException("Interrupted while scoring documents");
                }
                return phraseFreq() > 0; // custom – only necessary part left
            }

            @Override
            public float matchCost() {
                return 0; // custom – default value
            }
        };
    }

    @Override
    public float getMaxScore(int i) throws IOException {
        return 0;
    }

    @Override
    public DocIdSetIterator iterator() {
        return TwoPhaseIterator.asDocIdSetIterator(twoPhaseIterator());
    }

    @Override
    public String toString() {<FILL_FUNCTION_BODY>

        return "CustomExactPhraseScorer(" + weight + ")"; // custom – renamed class
    
<no_super_class>