FrequencyTrie.java

1
/*******************************************************************************
2
 * Copyright (C) 2026, Leo Galambos
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions are met:
7
 *
8
 * 1. Redistributions of source code must retain the above copyright notice,
9
 *    this list of conditions and the following disclaimer.
10
 *
11
 * 2. Redistributions in binary form must reproduce the above copyright notice,
12
 *    this list of conditions and the following disclaimer in the documentation
13
 *    and/or other materials provided with the distribution.
14
 *
15
 * 3. Neither the name of the copyright holder nor the names of its contributors
16
 *    may be used to endorse or promote products derived from this software
17
 *    without specific prior written permission.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
 * POSSIBILITY OF SUCH DAMAGE.
30
 ******************************************************************************/
31
package org.egothor.stemmer;
32
33
import java.io.DataInputStream;
34
import java.io.DataOutputStream;
35
import java.io.IOException;
36
import java.io.InputStream;
37
import java.io.OutputStream;
38
import java.nio.CharBuffer;
39
import java.nio.charset.StandardCharsets;
40
import java.security.MessageDigest;
41
import java.security.NoSuchAlgorithmException;
42
import java.util.ArrayList;
43
import java.util.Arrays;
44
import java.util.Collections;
45
import java.util.IdentityHashMap;
46
import java.util.LinkedHashMap;
47
import java.util.List;
48
import java.util.Locale;
49
import java.util.Map;
50
import java.util.Objects;
51
import java.util.concurrent.locks.ReentrantLock;
52
import java.util.function.IntFunction;
53
import java.util.logging.Level;
54
import java.util.logging.Logger;
55
56
import org.egothor.stemmer.trie.CompiledNode;
57
import org.egothor.stemmer.trie.LocalValueSummary;
58
import org.egothor.stemmer.trie.MutableNode;
59
import org.egothor.stemmer.trie.ReducedNode;
60
import org.egothor.stemmer.trie.ReductionContext;
61
import org.egothor.stemmer.trie.ReductionSignature;
62
63
/**
64
 * Read-only trie mapping {@link String} keys to one or more values with
65
 * frequency tracking.
66
 *
67
 * <p>
68
 * A key may be associated with multiple values. Each value keeps the number of
69
 * times it was inserted during the build phase. The method {@link #get(String)}
70
 * returns the locally most frequent value stored at the terminal node of the
71
 * supplied key, while {@link #getAll(String)} returns all locally stored values
72
 * ordered by descending frequency.
73
 *
74
 * <p>
75
 * If multiple values have the same local frequency, their ordering is
76
 * deterministic. The preferred value is selected by the following tie-breaking
77
 * rules, in order:
78
 * <ol>
79
 * <li>shorter {@link String} representation wins, based on
80
 * {@code value.toString()}</li>
81
 * <li>if the lengths are equal, lexicographically lower {@link String}
82
 * representation wins</li>
83
 * <li>if the textual representations are still equal, first-seen insertion
84
 * order remains stable</li>
85
 * </ol>
86
 *
87
 * <p>
88
 * Values may be stored at any trie node, including internal nodes and leaf
89
 * nodes. Therefore, reduction and canonicalization always operate on both the
90
 * node-local terminal values and the structure of all descendant edges.
91
 * </p>
92
 *
93
 * <p>
94
 * Instances are immutable and safe for concurrent lookup. A
95
 * {@linkplain #withLookupMode(LookupMode) lookup-mode view} shares the same
96
 * compiled node graph and changes only which applicable node or nodes a read
97
 * selects. Returned arrays and collections are caller-owned or immutable.
98
 * </p>
99
 *
100
 * @param <V> value type
101
 */
102
@SuppressWarnings({ "PMD.CyclomaticComplexity", "PMD.CouplingBetweenObjects" })
103
public final class FrequencyTrie<V> {
104
105
    /**
106
     * Logger of this class.
107
     */
108
    private static final Logger LOGGER = Logger.getLogger(FrequencyTrie.class.getName());
109
110
    /**
111
     * Domain separator used by the trie fingerprint canonical input.
112
     */
113
    private static final String FINGERPRINT_DOMAIN = "RADIXOR-FREQUENCY-TRIE-FINGERPRINT";
114
115
    /**
116
     * Version of the canonical fingerprint input format.
117
     */
118
    private static final int FINGERPRINT_FORMAT_VERSION = 2;
119
120
    /**
121
     * Root node of the compiled read-only trie.
122
     */
123
    private final CompiledNode<V> root;
124
125
    /**
126
     * Metadata persisted together with this trie.
127
     */
128
    private final TrieMetadata metadata;
129
130
    /**
131
     * Lazily initialized canonical SHA-256 fingerprint bytes. Every read and write
132
     * is guarded by {@link #fingerprintLock}. The value is assigned only after
133
     * calculation succeeds, so failed initialization leaves the cache empty. The
134
     * internal array is never exposed directly to callers.
135
     */
136
    private byte[] fingerprintBytes;
137
138
    /**
139
     * Guards all access to {@link #fingerprintBytes}. Holding this lock for every
140
     * cache read and write safely publishes successful lazy initialization to
141
     * subsequent callers.
142
     */
143
    private final ReentrantLock fingerprintLock = new ReentrantLock();
144
145
    /**
146
     * Cached traversal direction used for key lookup.
147
     */
148
    private final WordTraversalDirection lookupTraversalDirection;
149
150
    /**
151
     * Whether lookups require lowercase normalization.
152
     */
153
    private final boolean lowercasesLookupKeys;
154
155
    /**
156
     * Whether lookups require diacritic stripping.
157
     */
158
    private final boolean removeDiacritics;
159
160
    /**
161
     * Shared empty array instance for empty lookup results from
162
     * {@link #getAll(String)}.
163
     */
164
    private final V[] emptyValues;
165
166
    /**
167
     * Read-time command-selection policy for the {@code get} / {@code getAll}
168
     * family. Never persisted; defaults to {@link LookupMode#FIRST}.
169
     */
170
    private final LookupMode lookupMode;
171
172
    /**
173
     * Cached {@code true} when {@link #lookupTraversalDirection} consumes keys
174
     * from the end (BACKWARD), used by the {@code LAST}/{@code ALL} traversals.
175
     */
176
    private final boolean backwardLookup;
177
178
    /**
179
     * Binary format magic header.
180
     */
181
    private static final int STREAM_MAGIC = 0x45475452;
182
183
    /**
184
     * Minimum supported stream version constant retained for explicit range checks.
185
     */
186
    private static final int MIN_STREAM_VERSION = 1;
187
188
    /**
189
     * Number of stored values for which {@link #getEntries(String)} can return an
190
     * empty result.
191
     */
192
    private static final int NO_VALUE_COUNT = 0;
193
194
    /**
195
     * Number of stored values for which {@link #getEntries(String)} can use a
196
     * one-item immutable list special case.
197
     */
198
    private static final int SINGLE_VALUE_COUNT = 1;
199
200
    /**
201
     * Binary format version.
202
     */
203
    private static final int STREAM_VERSION = 7;
204
205
    /**
206
     * Version where traversal-direction ordinal is persisted.
207
     */
208
    private static final int TRAVERSAL_VERSION = 2;
209
210
    /**
211
     * Version where compact reduction metadata is persisted.
212
     */
213
    private static final int REDUCTION_VERSION = 3;
214
215
    /**
216
     * Version where case-processing mode ordinal is persisted.
217
     */
218
    private static final int CASE_VERSION = 4;
219
220
    /**
221
     * Version where the persisted metadata switched to a text block.
222
     */
223
    private static final int TEXT_METADATA_VERSION = 5;
224
225
    /**
226
     * Version where contracted accepting nodes are persisted.
227
     */
228
    private static final int ACCEPTING_NODE_VERSION = 6;
229
230
    /**
231
     * Version where distinct values are persisted once in a stream-local table.
232
     */
233
    private static final int VALUE_TABLE_VERSION = 7;
234
235
    /**
236
     * Argument name for lookup keys.
237
     */
238
    private static final String ARG_KEY = "key";
239
240
    /**
241
     * Default dense child lookup span in code points used when materializing
242
     * compiled nodes without an explicit override.
243
     * <p>
244
     * Increasing this value increases the chance of direct array indexing for child
245
     * lookup at runtime at the cost of per-node dense table memory for compact
246
     * character spans.
247
     * </p>
248
     */
249
    public static final int DEFAULT_MAX_EXPANDED_INDEX = 512;
250
251
    /**
252
     * Returns the current persisted binary stream format version.
253
     *
254
     * <p>
255
     * This method exists so other components can construct {@link TrieMetadata}
256
     * instances aligned with the currently written binary format without
257
     * duplicating constants.
258
     * </p>
259
     *
260
     * @return current trie stream format version
261
     */
262
    public static int currentFormatVersion() {
263 1 1. currentFormatVersion : replaced int return with 0 for org/egothor/stemmer/FrequencyTrie::currentFormatVersion → KILLED
        return STREAM_VERSION;
264
    }
265
266
    /**
267
     * Returns whether the supplied metadata identifies a stream format with a
268
     * serialized value table.
269
     *
270
     * @param metadata parsed trie metadata
271
     * @return {@code true} when values are stored in a stream-local table
272
     * @throws NullPointerException if {@code metadata} is {@code null}
273
     */
274
    /* default */ static boolean usesValueTableFormat(final TrieMetadata metadata) {
275 3 1. usesValueTableFormat : changed conditional boundary → SURVIVED
2. usesValueTableFormat : negated conditional → KILLED
3. usesValueTableFormat : replaced boolean return with true for org/egothor/stemmer/FrequencyTrie::usesValueTableFormat → KILLED
        return Objects.requireNonNull(metadata, "metadata").formatVersion() >= VALUE_TABLE_VERSION;
276
    }
277
278
    /**
279
     * Receives trie values during visitor-style lookup.
280
     *
281
     * <p>
282
     * Implementations are caller-owned and are not retained by the trie. Returning
283
     * {@code false} stops iteration after the current callback.
284
     * </p>
285
     *
286
     * @param <V> value type
287
     */
288
    @FunctionalInterface
289
    public interface EntrySink<V> {
290
291
        /**
292
         * Accepts one ordered local value.
293
         *
294
         * @param value stored value
295
         * @param count stored local occurrence count
296
         * @param rank  zero-based rank in deterministic local ordering
297
         * @return {@code true} to continue iteration, {@code false} to stop
298
         */
299
        boolean accept(V value, int count, int rank);
300
    }
301
302
    /**
303
     * Creates a new compiled trie instance.
304
     *
305
     * @param arrayFactory array factory
306
     * @param root         compiled root node
307
     * @param metadata     trie metadata describing lookup and persistence semantics
308
     * @throws NullPointerException if any argument is {@code null}
309
     */
310
    private FrequencyTrie(final IntFunction<V[]> arrayFactory, final CompiledNode<V> root,
311
            final TrieMetadata metadata) {
312
        this.root = Objects.requireNonNull(root, "root");
313
        this.metadata = Objects.requireNonNull(metadata, "metadata");
314
        this.lookupTraversalDirection = metadata.traversalDirection();
315 1 1. <init> : negated conditional → KILLED
        this.lowercasesLookupKeys = metadata.caseProcessingMode() == CaseProcessingMode.LOWERCASE_WITH_LOCALE_ROOT;
316 1 1. <init> : negated conditional → KILLED
        this.removeDiacritics = metadata.diacriticProcessingMode() == DiacriticProcessingMode.REMOVE;
317
        this.emptyValues = arrayFactory.apply(0);
318
        this.lookupMode = LookupMode.FIRST;
319 1 1. <init> : negated conditional → KILLED
        this.backwardLookup = this.lookupTraversalDirection == WordTraversalDirection.BACKWARD;
320
    }
321
322
    /**
323
     * Creates a lookup-policy view over an existing compiled trie, sharing its
324
     * immutable compiled structure and metadata.
325
     *
326
     * @param source original trie
327
     * @param mode   lookup policy for the new view
328
     */
329
    private FrequencyTrie(final FrequencyTrie<V> source, final LookupMode mode) {
330
        this.root = source.root;
331
        this.metadata = source.metadata;
332
        this.lookupTraversalDirection = source.lookupTraversalDirection;
333
        this.lowercasesLookupKeys = source.lowercasesLookupKeys;
334
        this.removeDiacritics = source.removeDiacritics;
335
        this.emptyValues = source.emptyValues;
336
        this.lookupMode = mode;
337
        this.backwardLookup = source.backwardLookup;
338
    }
339
340
    /**
341
     * Returns the read-time command-selection policy of this trie.
342
     *
343
     * <p>
344
     * The operation is constant-time and performs no allocation.
345
     * </p>
346
     *
347
     * @return current lookup mode
348
     */
349
    public LookupMode lookupMode() {
350 1 1. lookupMode : replaced return value with null for org/egothor/stemmer/FrequencyTrie::lookupMode → KILLED
        return this.lookupMode;
351
    }
352
353
    /**
354
     * Returns a view of this trie that selects commands according to {@code mode}.
355
     *
356
     * <p>
357
     * The returned instance shares this trie's immutable compiled structure, so
358
     * the operation is cheap and thread-safe. The policy is a read-time concern
359
     * and is never persisted (see {@link LookupMode}). Returns {@code this} when
360
     * the mode is unchanged.
361
     * </p>
362
     *
363
     * @apiNote Serializing this view writes the shared trie structure and metadata,
364
     *          but not {@code mode}. A subsequently loaded trie therefore starts in
365
     *          {@link LookupMode#FIRST}.
366
     *
367
     * @param mode command-selection policy
368
     * @return a trie view applying {@code mode}; returns this instance when the
369
     *         requested mode is already active
370
     * @throws NullPointerException if {@code mode} is {@code null}
371
     */
372
    public FrequencyTrie<V> withLookupMode(final LookupMode mode) {
373
        Objects.requireNonNull(mode, "mode");
374 1 1. withLookupMode : negated conditional → KILLED
        if (mode == this.lookupMode) {
375 1 1. withLookupMode : replaced return value with null for org/egothor/stemmer/FrequencyTrie::withLookupMode → KILLED
            return this;
376
        }
377 1 1. withLookupMode : replaced return value with null for org/egothor/stemmer/FrequencyTrie::withLookupMode → KILLED
        return new FrequencyTrie<>(this, mode);
378
    }
379
380
    /**
381
     * Creates a trie from an already compiled root.
382
     *
383
     * @param arrayFactory array factory
384
     * @param root         compiled root
385
     * @param metadata     trie metadata
386
     * @param <V>          value type
387
     * @return trie instance
388
     */
389
    /* default */ static <V> FrequencyTrie<V> fromCompiled(final IntFunction<V[]> arrayFactory,
390
            final CompiledNode<V> root,
391
            final TrieMetadata metadata) {
392 1 1. fromCompiled : replaced return value with null for org/egothor/stemmer/FrequencyTrie::fromCompiled → KILLED
        return new FrequencyTrie<>(arrayFactory, root, metadata);
393
    }
394
395
    /**
396
     * Returns the preferred value selected for the supplied key under this trie's
397
     * {@linkplain #lookupMode() lookup mode}.
398
     *
399
     * <p>
400
     * If multiple values have the same local frequency, the returned value is
401
     * selected deterministically by shorter {@code toString()} value first, then by
402
     * lexicographically lower {@code toString()}, and finally by stable first-seen
403
     * order.
404
     *
405
     * <p>
406
     * The supplied key is normalized according to persisted
407
     * {@link TrieMetadata#caseProcessingMode()} before traversal.
408
     * In {@link LookupMode#ALL}, this scalar operation uses the same
409
     * most-specific selection as {@link LookupMode#LAST}.
410
     * </p>
411
     *
412
     * @param key key to resolve
413
     * @return most frequent value, or {@code null} if the key does not exist or no
414
     *         value is stored at the addressed node
415
     * @throws NullPointerException if {@code key} is {@code null}
416
     */
417
    public V get(final String key) {
418
        Objects.requireNonNull(key, ARG_KEY);
419
        final String normalized = normalizeLookupKey(key);
420 1 1. get : negated conditional → KILLED
        final CompiledNode<V> node = this.lookupMode == LookupMode.FIRST
421
                ? findNode(normalized)
422
                : TrieLookup.findLast(this.root, this.backwardLookup, normalized);
423 1 1. get : negated conditional → KILLED
        if (node == null) {
424
            return null;
425
        }
426
        final V[] orderedValues = node.orderedValues();
427 1 1. get : negated conditional → KILLED
        if (orderedValues.length == 0) {
428
            return null;
429
        }
430 1 1. get : replaced return value with null for org/egothor/stemmer/FrequencyTrie::get → KILLED
        return orderedValues[0];
431
    }
432
433
    /**
434
     * Returns the preferred value for an already-normalized key.
435
     *
436
     * <p>
437
     * This method bypasses {@link TrieMetadata#caseProcessingMode()} and
438
     * {@link TrieMetadata#diacriticProcessingMode()}. Callers must supply input
439
     * normalized exactly as required by this trie's metadata. It is intended for
440
     * hot paths where normalization is guaranteed by an upstream tokenizer or
441
     * benchmark corpus and repeated lookup-time normalization would be redundant.
442
     * </p>
443
     *
444
     * @param key already-normalized key to resolve
445
     * @return most frequent value, or {@code null} if the key does not exist or no
446
     *         value is stored at the addressed node
447
     * @throws NullPointerException if {@code key} is {@code null}
448
     */
449
    public V getNormalized(final CharSequence key) {
450
        Objects.requireNonNull(key, ARG_KEY);
451 1 1. getNormalized : negated conditional → SURVIVED
        final CompiledNode<V> node = this.lookupMode == LookupMode.FIRST
452
                ? findNode(key)
453
                : TrieLookup.findLast(this.root, this.backwardLookup, key);
454 1 1. getNormalized : negated conditional → KILLED
        if (node == null) {
455
            return null;
456
        }
457
        final V[] orderedValues = node.orderedValues();
458 1 1. getNormalized : negated conditional → KILLED
        if (orderedValues.length == 0) {
459
            return null;
460
        }
461 1 1. getNormalized : replaced return value with null for org/egothor/stemmer/FrequencyTrie::getNormalized → KILLED
        return orderedValues[0];
462
    }
463
464
    /**
465
     * Returns the preferred value for an already-normalized {@link String} key.
466
     *
467
     * <p>
468
     * This overload keeps high-volume string lookup on a monomorphic path and
469
     * avoids the {@link CharSequence} dispatch used by the general overload.
470
     * Callers must supply input normalized exactly as required by this trie's
471
     * metadata.
472
     * </p>
473
     *
474
     * @param key already-normalized key to resolve
475
     * @return most frequent value, or {@code null} if the key does not exist or no
476
     *         value is stored at the addressed node
477
     * @throws NullPointerException if {@code key} is {@code null}
478
     */
479
    public V getNormalizedString(final String key) {
480
        Objects.requireNonNull(key, ARG_KEY);
481 1 1. getNormalizedString : negated conditional → SURVIVED
        final CompiledNode<V> node = this.lookupMode == LookupMode.FIRST
482
                ? findNode(key)
483
                : TrieLookup.findLast(this.root, this.backwardLookup, key);
484 1 1. getNormalizedString : negated conditional → KILLED
        if (node == null) {
485
            return null;
486
        }
487
        final V[] orderedValues = node.orderedValues();
488 1 1. getNormalizedString : negated conditional → KILLED
        if (orderedValues.length == 0) {
489
            return null;
490
        }
491 1 1. getNormalizedString : replaced return value with null for org/egothor/stemmer/FrequencyTrie::getNormalizedString → KILLED
        return orderedValues[0];
492
    }
493
494
    /**
495
     * Returns the values selected for the supplied key under this trie's
496
     * {@linkplain #lookupMode() lookup mode}.
497
     *
498
     * <p>
499
     * If multiple values have the same local frequency, the ordering is
500
     * deterministic by shorter {@code toString()} value first, then by
501
     * lexicographically lower {@code toString()}, and finally by stable first-seen
502
     * order.
503
     *
504
     * <p>
505
     * The returned array is a defensive copy.
506
     * Under {@link LookupMode#ALL}, values from all applicable nodes are ordered
507
     * from the most-specific node to the least-specific accepting ancestor,
508
     * retain each node's local frequency order, and are de-duplicated by
509
     * {@link Object#equals(Object)}. Other modes return one selected node's values.
510
     * </p>
511
     *
512
     * <p>
513
     * The supplied key is normalized according to persisted
514
     * {@link TrieMetadata#caseProcessingMode()} before traversal.
515
     * </p>
516
     *
517
     * @param key key to resolve
518
     * @return all values stored at the addressed node, ordered by descending
519
     *         frequency; returns an empty array if the key does not exist or no
520
     *         value is stored at the addressed node
521
     * @throws NullPointerException if {@code key} is {@code null}
522
     */
523
    @SuppressWarnings("PMD.MethodReturnsInternalArray")
524
    public V[] getAll(final String key) {
525
        Objects.requireNonNull(key, ARG_KEY);
526
        final String normalized = normalizeLookupKey(key);
527 1 1. getAll : negated conditional → KILLED
        if (this.lookupMode == LookupMode.ALL) {
528 1 1. getAll : replaced return value with null for org/egothor/stemmer/FrequencyTrie::getAll → KILLED
            return TrieLookup.collectAllValues(
529
                    TrieLookup.collectPath(this.root, this.backwardLookup, normalized), this.emptyValues);
530
        }
531 1 1. getAll : negated conditional → KILLED
        final CompiledNode<V> node = this.lookupMode == LookupMode.FIRST
532
                ? findNode(normalized)
533
                : TrieLookup.findLast(this.root, this.backwardLookup, normalized);
534 1 1. getAll : negated conditional → KILLED
        if (node == null) {
535 1 1. getAll : replaced return value with null for org/egothor/stemmer/FrequencyTrie::getAll → KILLED
            return this.emptyValues;
536
        }
537
        final V[] orderedValues = node.orderedValues();
538 1 1. getAll : negated conditional → KILLED
        if (orderedValues.length == 0) {
539 1 1. getAll : replaced return value with null for org/egothor/stemmer/FrequencyTrie::getAll → SURVIVED
            return this.emptyValues;
540
        }
541 1 1. getAll : replaced return value with null for org/egothor/stemmer/FrequencyTrie::getAll → KILLED
        return Arrays.copyOf(orderedValues, orderedValues.length);
542
    }
543
544
    /**
545
     * Returns the selected values and their occurrence counts, ordered by the same
546
     * rules as {@link #getAll(String)}.
547
     *
548
     * <p>
549
     * The returned list is aligned with the arrays returned by
550
     * {@link #getAll(String)} and the internal compiled count representation.
551
     *
552
     * <p>
553
     * The returned list is immutable.
554
     *
555
     * <p>
556
     * In reduction modes that merge semantically equivalent subtrees, the returned
557
     * counts may be aggregated across multiple original build-time nodes that were
558
     * reduced into the same canonical compiled node.
559
     * Under {@link LookupMode#ALL}, a value occurring at multiple applicable nodes
560
     * appears once with the count from its most-specific occurrence.
561
     * </p>
562
     *
563
     * @param key key to resolve
564
     * @return immutable ordered list of value-count entries; returns an empty list
565
     *         if the key does not exist or no value is stored at the addressed node
566
     * @throws NullPointerException if {@code key} is {@code null}
567
     */
568
    public List<ValueCount<V>> getEntries(final String key) {
569
        Objects.requireNonNull(key, ARG_KEY);
570
        final String normalized = normalizeLookupKey(key);
571 1 1. getEntries : negated conditional → KILLED
        if (this.lookupMode == LookupMode.ALL) {
572 1 1. getEntries : replaced return value with Collections.emptyList for org/egothor/stemmer/FrequencyTrie::getEntries → KILLED
            return TrieLookup.collectAllEntries(
573
                    TrieLookup.collectPath(this.root, this.backwardLookup, normalized));
574
        }
575 1 1. getEntries : negated conditional → SURVIVED
        final CompiledNode<V> node = this.lookupMode == LookupMode.FIRST
576
                ? findNode(normalized)
577
                : TrieLookup.findLast(this.root, this.backwardLookup, normalized);
578 1 1. getEntries : negated conditional → KILLED
        if (node == null) {
579
            return List.of();
580
        }
581
582
        final V[] orderedValues = node.orderedValues();
583
        final int valueCount = orderedValues.length;
584 1 1. getEntries : negated conditional → KILLED
        if (valueCount == NO_VALUE_COUNT) {
585
            return List.of();
586
        }
587
588 1 1. getEntries : negated conditional → KILLED
        if (valueCount == SINGLE_VALUE_COUNT) {
589 1 1. getEntries : replaced return value with Collections.emptyList for org/egothor/stemmer/FrequencyTrie::getEntries → KILLED
            return List.of(new ValueCount<>(orderedValues[0], node.orderedCounts()[0]));
590
        }
591
592
        final int[] orderedCounts = node.orderedCounts();
593
        final List<ValueCount<V>> entries = new ArrayList<>(valueCount);
594 2 1. getEntries : changed conditional boundary → KILLED
2. getEntries : negated conditional → KILLED
        for (int index = 0; index < valueCount; index++) {
595
            entries.add(new ValueCount<>(orderedValues[index], orderedCounts[index]));
596
        }
597 1 1. getEntries : replaced return value with Collections.emptyList for org/egothor/stemmer/FrequencyTrie::getEntries → KILLED
        return Collections.unmodifiableList(entries);
598
    }
599
600
    /**
601
     * Visits all values stored at the node addressed by an already-normalized
602
     * {@code char[]} key slice.
603
     *
604
     * <p>
605
     * This method bypasses {@link TrieMetadata#caseProcessingMode()} and
606
     * {@link TrieMetadata#diacriticProcessingMode()}. The caller must provide input
607
     * normalized exactly as required by this trie's metadata. The trie is immutable
608
     * and thread-safe for concurrent reads; the supplied sink is caller-owned and
609
     * is not retained.
610
     * </p>
611
     *
612
     * @param key        normalized key storage
613
     * @param offset     first character offset
614
     * @param length     number of characters to read
615
     * @param sink       value sink
616
     * @param maxResults maximum number of results to visit
617
     * @return number of visited values
618
     * @throws NullPointerException      if {@code key} or {@code sink} is
619
     *                                   {@code null}
620
     * @throws IndexOutOfBoundsException if the key slice is invalid
621
     * @throws IllegalArgumentException  if {@code maxResults} is negative
622
     */
623
    public int getAllNormalized(final char[] key, final int offset, final int length, final EntrySink<? super V> sink,
624
            final int maxResults) {
625
        Objects.requireNonNull(key, ARG_KEY);
626
        Objects.requireNonNull(sink, "sink");
627
        Objects.checkFromIndexSize(offset, length, key.length);
628 1 1. getAllNormalized : removed call to org/egothor/stemmer/FrequencyTrie::validateMaxResults → SURVIVED
        validateMaxResults(maxResults);
629 1 1. getAllNormalized : negated conditional → KILLED
        if (maxResults == 0) {
630
            return 0;
631
        }
632 1 1. getAllNormalized : negated conditional → SURVIVED
        if (this.lookupMode == LookupMode.ALL) {
633 1 1. getAllNormalized : replaced int return with 0 for org/egothor/stemmer/FrequencyTrie::getAllNormalized → NO_COVERAGE
            return TrieLookup.visitNodes(
634
                    TrieLookup.collectPath(this.root, this.backwardLookup, CharBuffer.wrap(key, offset, length)),
635
                    sink, maxResults);
636
        }
637 1 1. getAllNormalized : negated conditional → SURVIVED
        final CompiledNode<V> node = this.lookupMode == LookupMode.FIRST
638
                ? findNode(key, offset, length)
639
                : TrieLookup.findLast(this.root, this.backwardLookup, key, offset, length);
640 1 1. getAllNormalized : replaced int return with 0 for org/egothor/stemmer/FrequencyTrie::getAllNormalized → KILLED
        return visitNode(node, sink, maxResults);
641
    }
642
643
    /**
644
     * Visits all values stored at the node addressed by an already-normalized
645
     * character sequence.
646
     *
647
     * @param key        normalized key
648
     * @param sink       value sink
649
     * @param maxResults maximum number of results to visit
650
     * @return number of visited values
651
     * @throws NullPointerException     if {@code key} or {@code sink} is
652
     *                                  {@code null}
653
     * @throws IllegalArgumentException if {@code maxResults} is negative
654
     * @see #getAllNormalized(char[], int, int, EntrySink, int)
655
     */
656
    public int getAllNormalized(final CharSequence key, final EntrySink<? super V> sink, final int maxResults) {
657
        Objects.requireNonNull(key, ARG_KEY);
658
        Objects.requireNonNull(sink, "sink");
659 1 1. getAllNormalized : removed call to org/egothor/stemmer/FrequencyTrie::validateMaxResults → KILLED
        validateMaxResults(maxResults);
660 1 1. getAllNormalized : negated conditional → KILLED
        if (maxResults == 0) {
661
            return 0;
662
        }
663 1 1. getAllNormalized : negated conditional → SURVIVED
        if (this.lookupMode == LookupMode.ALL) {
664 1 1. getAllNormalized : replaced int return with 0 for org/egothor/stemmer/FrequencyTrie::getAllNormalized → NO_COVERAGE
            return TrieLookup.visitNodes(
665
                    TrieLookup.collectPath(this.root, this.backwardLookup, key), sink, maxResults);
666
        }
667 1 1. getAllNormalized : negated conditional → SURVIVED
        final CompiledNode<V> node = this.lookupMode == LookupMode.FIRST
668
                ? findNode(key)
669
                : TrieLookup.findLast(this.root, this.backwardLookup, key);
670 1 1. getAllNormalized : replaced int return with 0 for org/egothor/stemmer/FrequencyTrie::getAllNormalized → KILLED
        return visitNode(node, sink, maxResults);
671
    }
672
673
    /**
674
     * Visits the first value stored at the node addressed by an already-normalized
675
     * {@code char[]} key slice.
676
     *
677
     * @param key    normalized key storage
678
     * @param offset first character offset
679
     * @param length number of characters to read
680
     * @param sink   value sink
681
     * @return {@code true} when a value was visited, otherwise {@code false}
682
     * @see #getAllNormalized(char[], int, int, EntrySink, int)
683
     */
684
    public boolean getFirstNormalized(final char[] key, final int offset, final int length,
685
            final EntrySink<? super V> sink) {
686 2 1. getFirstNormalized : replaced boolean return with true for org/egothor/stemmer/FrequencyTrie::getFirstNormalized → NO_COVERAGE
2. getFirstNormalized : negated conditional → NO_COVERAGE
        return getAllNormalized(key, offset, length, sink, 1) == 1;
687
    }
688
689
    /**
690
     * Visits the first value stored at the node addressed by an already-normalized
691
     * character sequence.
692
     *
693
     * @param key  normalized key
694
     * @param sink value sink
695
     * @return {@code true} when a value was visited, otherwise {@code false}
696
     * @see #getAllNormalized(CharSequence, EntrySink, int)
697
     */
698
    public boolean getFirstNormalized(final CharSequence key, final EntrySink<? super V> sink) {
699 2 1. getFirstNormalized : negated conditional → KILLED
2. getFirstNormalized : replaced boolean return with true for org/egothor/stemmer/FrequencyTrie::getFirstNormalized → KILLED
        return getAllNormalized(key, sink, 1) == 1;
700
    }
701
702
    /**
703
     * Visits all values stored at the node addressed by the supplied key, applying
704
     * metadata-driven lookup normalization when required.
705
     *
706
     * <p>
707
     * This method preserves the same lookup normalization semantics as
708
     * {@link #getAll(String)}. It may allocate when metadata requires lowercase or
709
     * diacritic normalization.
710
     * </p>
711
     *
712
     * @param key        key to resolve
713
     * @param sink       value sink
714
     * @param maxResults maximum number of results to visit
715
     * @return number of visited values
716
     */
717
    public int getAll(final CharSequence key, final EntrySink<? super V> sink, final int maxResults) {
718
        Objects.requireNonNull(key, ARG_KEY);
719
        Objects.requireNonNull(sink, "sink");
720 1 1. getAll : removed call to org/egothor/stemmer/FrequencyTrie::validateMaxResults → KILLED
        validateMaxResults(maxResults);
721 1 1. getAll : negated conditional → KILLED
        if (maxResults == 0) {
722
            return 0;
723
        }
724
        final CharSequence normalized = normalizeLookupKey(key);
725 1 1. getAll : negated conditional → SURVIVED
        if (this.lookupMode == LookupMode.ALL) {
726 1 1. getAll : replaced int return with 0 for org/egothor/stemmer/FrequencyTrie::getAll → NO_COVERAGE
            return TrieLookup.visitNodes(
727
                    TrieLookup.collectPath(this.root, this.backwardLookup, normalized), sink, maxResults);
728
        }
729 1 1. getAll : negated conditional → SURVIVED
        final CompiledNode<V> node = this.lookupMode == LookupMode.FIRST
730
                ? findNode(normalized)
731
                : TrieLookup.findLast(this.root, this.backwardLookup, normalized);
732 1 1. getAll : replaced int return with 0 for org/egothor/stemmer/FrequencyTrie::getAll → KILLED
        return visitNode(node, sink, maxResults);
733
    }
734
735
    /**
736
     * Visits the first value stored at the node addressed by the supplied key,
737
     * applying metadata-driven lookup normalization when required.
738
     *
739
     * @param key  key to resolve
740
     * @param sink value sink
741
     * @return {@code true} when a value was visited, otherwise {@code false}
742
     * @see #getAll(CharSequence, EntrySink, int)
743
     */
744
    public boolean getFirst(final CharSequence key, final EntrySink<? super V> sink) {
745 2 1. getFirst : replaced boolean return with true for org/egothor/stemmer/FrequencyTrie::getFirst → SURVIVED
2. getFirst : negated conditional → KILLED
        return getAll(key, sink, 1) == 1;
746
    }
747
748
    /**
749
     * Returns the logical key traversal direction used by this trie.
750
     *
751
     * <p>
752
     * The same direction must be used when reconstructing mutable builders or when
753
     * applying patch commands that were generated against keys stored in this trie.
754
     * </p>
755
     *
756
     * @return logical key traversal direction
757
     */
758
    public WordTraversalDirection traversalDirection() {
759 1 1. traversalDirection : replaced return value with null for org/egothor/stemmer/FrequencyTrie::traversalDirection → KILLED
        return this.metadata.traversalDirection();
760
    }
761
762
    /**
763
     * Returns immutable persisted metadata associated with this trie.
764
     *
765
     * @return trie metadata
766
     */
767
    public TrieMetadata metadata() {
768 1 1. metadata : replaced return value with null for org/egothor/stemmer/FrequencyTrie::metadata → KILLED
        return this.metadata;
769
    }
770
771
    /**
772
     * Returns the deterministic SHA-256 fingerprint of this trie.
773
     *
774
     * <p>
775
     * The fingerprint is a canonical model identity, not a Java object identity. It
776
     * includes a fingerprint-domain marker, the fingerprint input format version,
777
     * persisted metadata, and the complete compiled-node structure reachable from
778
     * the root, including edges, child references, local values, and local counts.
779
     * </p>
780
     *
781
     * <p>
782
     * The returned value is stable across JVM runs for equivalent trie content and
783
     * metadata. It does not include object identity, memory layout, runtime cache
784
     * state, absolute file paths, timestamps, or other process-local state.
785
     * </p>
786
     *
787
     * <p>
788
     * The fingerprint is calculated on first request and reused by later
789
     * fingerprint accessors.
790
     * </p>
791
     *
792
     * @return 64-character lowercase hexadecimal SHA-256 fingerprint
793
     */
794
    public String getFingerprint() {
795 1 1. getFingerprint : replaced return value with "" for org/egothor/stemmer/FrequencyTrie::getFingerprint → KILLED
        return toLowerHex(fingerprintBytes());
796
    }
797
798
    /**
799
     * Returns a defensive copy of the raw SHA-256 fingerprint bytes.
800
     *
801
     * <p>
802
     * The returned array has length {@code 32}. Mutating it does not affect this
803
     * trie.
804
     * </p>
805
     *
806
     * <p>
807
     * The fingerprint is calculated on first request and reused by later
808
     * fingerprint accessors.
809
     * </p>
810
     *
811
     * @return defensive copy of the 32-byte SHA-256 fingerprint
812
     */
813
    public byte[] copyFingerprintBytes() {
814
        final byte[] localFingerprintBytes = fingerprintBytes();
815 1 1. copyFingerprintBytes : replaced return value with null for org/egothor/stemmer/FrequencyTrie::copyFingerprintBytes → KILLED
        return Arrays.copyOf(localFingerprintBytes, localFingerprintBytes.length);
816
    }
817
818
    /**
819
     * Returns the cached raw SHA-256 fingerprint bytes, computing them on the first
820
     * request.
821
     *
822
     * <p>
823
     * Access to the cache is guarded by {@link #fingerprintLock}. Because every read
824
     * and write of the cache occurs while holding the same lock, successful
825
     * initialization is safely published to all subsequent callers. If calculation
826
     * fails, no value is cached and a later call may retry.
827
     * </p>
828
     *
829
     * @return internal cached raw SHA-256 fingerprint bytes
830
     */
831
    private byte[] fingerprintBytes() {
832 1 1. fingerprintBytes : removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED
        this.fingerprintLock.lock();
833
        try {
834
            byte[] localFingerprintBytes = this.fingerprintBytes;
835 1 1. fingerprintBytes : negated conditional → KILLED
            if (localFingerprintBytes == null) {
836
                localFingerprintBytes = computeFingerprintBytes(this.root, this.metadata);
837
                this.fingerprintBytes = localFingerprintBytes;
838
            }
839 1 1. fingerprintBytes : replaced return value with null for org/egothor/stemmer/FrequencyTrie::fingerprintBytes → KILLED
            return localFingerprintBytes;
840
        } finally {
841 1 1. fingerprintBytes : removed call to java/util/concurrent/locks/ReentrantLock::unlock → TIMED_OUT
            this.fingerprintLock.unlock();
842
        }
843
    }
844
845
    private static <V> byte[] computeFingerprintBytes(final CompiledNode<V> root, final TrieMetadata metadata) {
846
        final MessageDigest messageDigest = newSha256Digest();
847 1 1. computeFingerprintBytes : removed call to org/egothor/stemmer/FrequencyTrie::updateUtf8 → SURVIVED
        updateUtf8(messageDigest, FINGERPRINT_DOMAIN);
848 1 1. computeFingerprintBytes : removed call to org/egothor/stemmer/FrequencyTrie::updateInt → SURVIVED
        updateInt(messageDigest, FINGERPRINT_FORMAT_VERSION);
849 1 1. computeFingerprintBytes : removed call to org/egothor/stemmer/FrequencyTrie::updateUtf8 → SURVIVED
        updateUtf8(messageDigest, metadata.toTextBlock());
850
851
        final Map<CompiledNode<V>, Integer> nodeIds = new IdentityHashMap<>();
852
        final List<CompiledNode<V>> orderedNodes = new ArrayList<>();
853 1 1. computeFingerprintBytes : removed call to org/egothor/stemmer/FrequencyTrie::assignNodeIds → KILLED
        assignNodeIds(root, nodeIds, orderedNodes);
854
855 1 1. computeFingerprintBytes : removed call to org/egothor/stemmer/FrequencyTrie::updateInt → SURVIVED
        updateInt(messageDigest, nodeIds.get(root));
856 1 1. computeFingerprintBytes : removed call to org/egothor/stemmer/FrequencyTrie::updateInt → SURVIVED
        updateInt(messageDigest, orderedNodes.size());
857
        for (CompiledNode<V> node : orderedNodes) {
858 1 1. computeFingerprintBytes : removed call to org/egothor/stemmer/FrequencyTrie::updateNodeFingerprint → KILLED
            updateNodeFingerprint(messageDigest, node, nodeIds);
859
        }
860 1 1. computeFingerprintBytes : replaced return value with null for org/egothor/stemmer/FrequencyTrie::computeFingerprintBytes → KILLED
        return messageDigest.digest();
861
    }
862
863
    /**
864
     * Returns the root node mainly for diagnostics and tests within the package.
865
     *
866
     * @return compiled root node
867
     */
868
    /* default */ CompiledNode<V> root() {
869 1 1. root : replaced return value with null for org/egothor/stemmer/FrequencyTrie::root → KILLED
        return this.root;
870
    }
871
872
    /**
873
     * Writes this compiled trie to the supplied output stream.
874
     *
875
     * <p>
876
     * The binary format is versioned and preserves canonical shared compiled nodes,
877
     * therefore the serialized representation remains compact even for tries
878
     * reduced by subtree merging.
879
     *
880
     * <p>
881
     * The supplied codec is responsible for persisting individual values of type
882
     * {@code V}.
883
     *
884
     * @param outputStream target output stream
885
     * @param valueCodec   codec used to write values
886
     * @throws NullPointerException if any argument is {@code null}
887
     * @throws IOException          if writing fails
888
     */
889
    public void writeTo(final OutputStream outputStream, final ValueStreamCodec<V> valueCodec) throws IOException {
890
        Objects.requireNonNull(outputStream, "outputStream");
891
        Objects.requireNonNull(valueCodec, "valueCodec");
892
893
        final DataOutputStream dataOutput; // NOPMD
894 1 1. writeTo : negated conditional → KILLED
        if (outputStream instanceof DataOutputStream) {
895
            dataOutput = (DataOutputStream) outputStream;
896
        } else {
897
            dataOutput = new DataOutputStream(outputStream);
898
        }
899
900
        final Map<CompiledNode<V>, Integer> nodeIds = new IdentityHashMap<>();
901
        final List<CompiledNode<V>> orderedNodes = new ArrayList<>();
902 1 1. writeTo : removed call to org/egothor/stemmer/FrequencyTrie::assignNodeIds → KILLED
        assignNodeIds(this.root, nodeIds, orderedNodes);
903
        final Map<V, Integer> valueIds = new LinkedHashMap<>();
904
        final List<V> distinctValues = new ArrayList<>();
905 1 1. writeTo : removed call to org/egothor/stemmer/FrequencyTrie::collectDistinctValues → KILLED
        collectDistinctValues(orderedNodes, valueIds, distinctValues);
906
907
        if (LOGGER.isLoggable(Level.FINE)) {
908
            LOGGER.log(Level.FINE, "Writing compiled trie with {0} canonical nodes.", orderedNodes.size());
909
        }
910
911 1 1. writeTo : removed call to java/io/DataOutputStream::writeInt → KILLED
        dataOutput.writeInt(STREAM_MAGIC);
912 1 1. writeTo : removed call to java/io/DataOutputStream::writeInt → KILLED
        dataOutput.writeInt(STREAM_VERSION);
913 1 1. writeTo : removed call to java/io/DataOutputStream::writeInt → KILLED
        dataOutput.writeInt(orderedNodes.size());
914 1 1. writeTo : removed call to java/io/DataOutputStream::writeInt → KILLED
        dataOutput.writeInt(nodeIds.get(this.root));
915 1 1. writeTo : removed call to org/egothor/stemmer/FrequencyTrie::writeMetadata → KILLED
        writeMetadata(dataOutput, metadataForCurrentStream(this.metadata));
916 1 1. writeTo : removed call to org/egothor/stemmer/FrequencyTrie::writeValueTable → KILLED
        writeValueTable(dataOutput, valueCodec, distinctValues);
917
918 2 1. writeTo : negated conditional → KILLED
2. writeTo : changed conditional boundary → KILLED
        for (int nodeId = 0; nodeId < orderedNodes.size(); nodeId++) {
919 1 1. writeTo : removed call to org/egothor/stemmer/FrequencyTrie::writeNode → KILLED
            writeNode(dataOutput, orderedNodes.get(nodeId), nodeId, nodeIds, valueIds);
920
        }
921
922 1 1. writeTo : removed call to java/io/DataOutputStream::flush → SURVIVED
        dataOutput.flush();
923
    }
924
925
    /**
926
     * Reads a compiled trie from the supplied input stream.
927
     *
928
     * <p>
929
     * The caller must provide the same value codec semantics that were used during
930
     * persistence as well as the array factory required for typed result arrays.
931
     *
932
     * @param inputStream  source input stream
933
     * @param arrayFactory factory used to create typed arrays
934
     * @param valueCodec   codec used to read values
935
     * @param <V>          value type
936
     * @return deserialized compiled trie
937
     * @throws NullPointerException if any argument is {@code null}
938
     * @throws IOException          if reading fails or the binary format is invalid
939
     */
940
    public static <V> FrequencyTrie<V> readFrom(final InputStream inputStream, final IntFunction<V[]> arrayFactory,
941
            final ValueStreamCodec<V> valueCodec) throws IOException {
942 1 1. readFrom : replaced return value with null for org/egothor/stemmer/FrequencyTrie::readFrom → KILLED
        return readFrom(inputStream, arrayFactory, valueCodec, -1);
943
    }
944
945
    /**
946
     * Reads a compiled trie from the supplied input stream, optionally overriding
947
     * dense child-index span configuration.
948
     * <p>
949
     * This setting is applied only while materializing the in-memory compiled
950
     * representation during load. It is not serialized in {@link TrieMetadata}, so
951
     * each load can independently choose its own runtime lookup trade-off.
952
     * </p>
953
     *
954
     * @param inputStream      source input stream
955
     * @param arrayFactory     array factory used to create typed arrays
956
     * @param valueCodec       codec used to read values
957
     * @param maxExpandedIndex dense lookup span override; zero disables dense
958
     *                         lookup, negative values use
959
     *                         {@link #DEFAULT_MAX_EXPANDED_INDEX}
960
     * @param <V>              value type
961
     * @return deserialized compiled trie
962
     * @throws NullPointerException if any argument is {@code null}
963
     * @throws IOException          if reading fails or the binary format is invalid
964
     */
965
    public static <V> FrequencyTrie<V> readFrom(final InputStream inputStream, final IntFunction<V[]> arrayFactory,
966
            final ValueStreamCodec<V> valueCodec, final int maxExpandedIndex) throws IOException {
967
        Objects.requireNonNull(valueCodec, "valueCodec");
968 1 1. readFrom : replaced return value with null for org/egothor/stemmer/FrequencyTrie::readFrom → KILLED
        return readFromWithMetadata(inputStream, arrayFactory,
969 1 1. lambda$readFrom$0 : replaced return value with null for org/egothor/stemmer/FrequencyTrie::lambda$readFrom$0 → KILLED
                (dataInput, metadata) -> valueCodec.read(dataInput), maxExpandedIndex);
970
    }
971
972
    /**
973
     * Reads a compiled trie while allowing value decoding to use already parsed
974
     * trie metadata.
975
     *
976
     * <p>
977
     * This package-private path materializes the requested final value type during
978
     * the normal graph read. It does not expose reader state or construct an
979
     * intermediate trie with a different value type.
980
     * </p>
981
     *
982
     * @param inputStream      source input stream
983
     * @param arrayFactory     factory used to create typed value arrays
984
     * @param valueReader      metadata-aware value reader
985
     * @param maxExpandedIndex dense lookup span override; zero disables dense
986
     *                         lookup, negative values use
987
     *                         {@link #DEFAULT_MAX_EXPANDED_INDEX}
988
     * @param <V>              final value type
989
     * @return deserialized compiled trie containing values returned by
990
     *         {@code valueReader}
991
     * @throws NullPointerException if any argument is {@code null}
992
     * @throws IOException          if reading fails or the binary format is invalid
993
     */
994
    /* default */ static <V> FrequencyTrie<V> readFromWithMetadata(final InputStream inputStream,
995
            final IntFunction<V[]> arrayFactory, final MetadataValueStreamReader<V> valueReader,
996
            final int maxExpandedIndex) throws IOException {
997 1 1. readFromWithMetadata : replaced return value with null for org/egothor/stemmer/FrequencyTrie::readFromWithMetadata → KILLED
        return CompiledTrieReader.read(inputStream, arrayFactory, valueReader, maxExpandedIndex);
998
    }
999
1000
    /**
1001
     * Writes persisted trie metadata.
1002
     *
1003
     * @param dataOutput output stream
1004
     * @param metadata   metadata to serialize
1005
     * @throws IOException if writing fails
1006
     */
1007
    private static void writeMetadata(final DataOutputStream dataOutput, final TrieMetadata metadata)
1008
            throws IOException {
1009 1 1. writeMetadata : removed call to java/io/DataOutputStream::writeUTF → KILLED
        dataOutput.writeUTF(metadata.toTextBlock());
1010
    }
1011
1012
    /**
1013
     * Creates metadata aligned with the stream version emitted by the current
1014
     * writer.
1015
     *
1016
     * <p>
1017
     * The returned metadata preserves every semantic setting from the trie while
1018
     * reporting the current binary format version. The immutable metadata stored by
1019
     * the trie is not modified.
1020
     * </p>
1021
     *
1022
     * @param metadata source trie metadata
1023
     * @return metadata aligned with {@link #STREAM_VERSION}
1024
     */
1025
    private static TrieMetadata metadataForCurrentStream(final TrieMetadata metadata) {
1026 1 1. metadataForCurrentStream : replaced return value with null for org/egothor/stemmer/FrequencyTrie::metadataForCurrentStream → KILLED
        return new TrieMetadata(STREAM_VERSION, metadata.traversalDirection(), metadata.reductionSettings(),
1027
                metadata.diacriticProcessingMode(), metadata.caseProcessingMode());
1028
    }
1029
1030
    /**
1031
     * Returns the number of canonical compiled nodes reachable from the root.
1032
     *
1033
     * <p>
1034
     * The returned value reflects the size of the final reduced immutable trie, not
1035
     * the number of mutable build-time nodes inserted before reduction. Shared
1036
     * canonical subtrees are counted only once.
1037
     *
1038
     * @return number of canonical compiled nodes in this trie
1039
     */
1040
    public int size() {
1041
        final Map<CompiledNode<V>, Integer> nodeIds = new IdentityHashMap<>();
1042
        final List<CompiledNode<V>> orderedNodes = new ArrayList<>();
1043 1 1. size : removed call to org/egothor/stemmer/FrequencyTrie::assignNodeIds → KILLED
        assignNodeIds(this.root, nodeIds, orderedNodes);
1044 1 1. size : replaced int return with 0 for org/egothor/stemmer/FrequencyTrie::size → KILLED
        return orderedNodes.size();
1045
    }
1046
1047
    /**
1048
     * Assigns deterministic identifiers to all canonical compiled nodes reachable
1049
     * from the supplied root.
1050
     *
1051
     * @param node         current node
1052
     * @param nodeIds      assigned node identifiers
1053
     * @param orderedNodes ordered nodes in identifier order
1054
     */
1055
    private static <V> void assignNodeIds(final CompiledNode<V> node, final Map<CompiledNode<V>, Integer> nodeIds,
1056
            final List<CompiledNode<V>> orderedNodes) {
1057 1 1. assignNodeIds : negated conditional → KILLED
        if (nodeIds.containsKey(node)) {
1058
            return;
1059
        }
1060
1061
        final int nodeId = orderedNodes.size();
1062
        nodeIds.put(node, nodeId);
1063
        orderedNodes.add(node);
1064
1065
        for (CompiledNode<V> child : node.children()) {
1066 1 1. assignNodeIds : removed call to org/egothor/stemmer/FrequencyTrie::assignNodeIds → KILLED
            assignNodeIds(child, nodeIds, orderedNodes);
1067
        }
1068
    }
1069
1070
    /**
1071
     * Collects the deterministic equality-based value table for serialization.
1072
     *
1073
     * <p>
1074
     * Nodes are visited in canonical node-identifier order and values are visited
1075
     * in their existing node-local order. The first occurrence according to
1076
     * {@link Object#equals(Object)} and {@link Object#hashCode()} assigns the table
1077
     * index.
1078
     * </p>
1079
     *
1080
     * @param orderedNodes   canonical nodes in identifier order
1081
     * @param valueIds       destination mapping from values to table indexes
1082
     * @param distinctValues destination values in table-index order
1083
     * @param <V>            value type
1084
     */
1085
    private static <V> void collectDistinctValues(final List<CompiledNode<V>> orderedNodes,
1086
            final Map<V, Integer> valueIds, final List<V> distinctValues) {
1087
        for (CompiledNode<V> node : orderedNodes) {
1088
            for (V value : node.orderedValues()) {
1089 1 1. collectDistinctValues : negated conditional → KILLED
                if (!valueIds.containsKey(value)) {
1090
                    final int valueId = distinctValues.size();
1091
                    valueIds.put(value, valueId);
1092
                    distinctValues.add(value);
1093
                }
1094
            }
1095
        }
1096
    }
1097
1098
    /**
1099
     * Writes every distinct value exactly once in table-index order.
1100
     *
1101
     * @param dataOutput     output stream
1102
     * @param valueCodec     codec responsible for value encoding
1103
     * @param distinctValues distinct values in deterministic table order
1104
     * @param <V>            value type
1105
     * @throws IOException if writing the table fails
1106
     */
1107
    private static <V> void writeValueTable(final DataOutputStream dataOutput, final ValueStreamCodec<V> valueCodec,
1108
            final List<V> distinctValues) throws IOException {
1109 1 1. writeValueTable : removed call to java/io/DataOutputStream::writeInt → KILLED
        dataOutput.writeInt(distinctValues.size());
1110
        for (V value : distinctValues) {
1111 1 1. writeValueTable : removed call to org/egothor/stemmer/FrequencyTrie$ValueStreamCodec::write → KILLED
            valueCodec.write(dataOutput, value);
1112
        }
1113
    }
1114
1115
    /**
1116
     * Writes one compiled node using stream-local value-table indexes.
1117
     *
1118
     * @param dataOutput output
1119
     * @param node       node to write
1120
     * @param nodeId     canonical identifier of {@code node}
1121
     * @param nodeIds    node identifiers
1122
     * @param valueIds   value-table indexes
1123
     * @param <V>        value type
1124
     * @throws IOException if writing fails
1125
     */
1126
    private static <V> void writeNode(final DataOutputStream dataOutput, final CompiledNode<V> node, final int nodeId,
1127
            final Map<CompiledNode<V>, Integer> nodeIds, final Map<V, Integer> valueIds) throws IOException {
1128 1 1. writeNode : removed call to java/io/DataOutputStream::writeBoolean → KILLED
        dataOutput.writeBoolean(node.acceptsRemainingInput());
1129 1 1. writeNode : removed call to java/io/DataOutputStream::writeInt → KILLED
        dataOutput.writeInt(node.edgeLabels().length);
1130 2 1. writeNode : changed conditional boundary → KILLED
2. writeNode : negated conditional → KILLED
        for (int index = 0; index < node.edgeLabels().length; index++) {
1131 1 1. writeNode : removed call to java/io/DataOutputStream::writeChar → KILLED
            dataOutput.writeChar(node.edgeLabels()[index]);
1132
            final Integer childNodeId = nodeIds.get(node.children()[index]);
1133 1 1. writeNode : negated conditional → KILLED
            if (childNodeId == null) {
1134
                throw new IOException("Missing child node identifier during serialization.");
1135
            }
1136 1 1. writeNode : removed call to java/io/DataOutputStream::writeInt → KILLED
            dataOutput.writeInt(childNodeId);
1137
        }
1138
1139 1 1. writeNode : removed call to java/io/DataOutputStream::writeInt → KILLED
        dataOutput.writeInt(node.orderedValues().length);
1140 2 1. writeNode : changed conditional boundary → KILLED
2. writeNode : negated conditional → KILLED
        for (int index = 0; index < node.orderedValues().length; index++) {
1141
            final V value = node.orderedValues()[index];
1142
            final Integer valueId = valueIds.get(value);
1143 1 1. writeNode : negated conditional → KILLED
            if (valueId == null) {
1144 1 1. writeNode : negated conditional → NO_COVERAGE
                final String valueContext = value == null ? "null"
1145
                        : value.getClass().getName() + '[' + value + ']';
1146
                throw new IOException("Missing value table index at canonical node " + nodeId + ", local value "
1147
                        + index + ": " + valueContext);
1148
            }
1149 1 1. writeNode : removed call to java/io/DataOutputStream::writeInt → KILLED
            dataOutput.writeInt(valueId);
1150 1 1. writeNode : removed call to java/io/DataOutputStream::writeInt → KILLED
            dataOutput.writeInt(node.orderedCounts()[index]);
1151
        }
1152
    }
1153
1154
    private static MessageDigest newSha256Digest() {
1155
        try {
1156 1 1. newSha256Digest : replaced return value with null for org/egothor/stemmer/FrequencyTrie::newSha256Digest → KILLED
            return MessageDigest.getInstance("SHA-256");
1157
        } catch (NoSuchAlgorithmException exception) {
1158
            throw new IllegalStateException("SHA-256 digest is not available.", exception);
1159
        }
1160
    }
1161
1162
    private static <V> void updateNodeFingerprint(final MessageDigest messageDigest, final CompiledNode<V> node,
1163
            final Map<CompiledNode<V>, Integer> nodeIds) {
1164
        final char[] edgeLabels = node.edgeLabels();
1165
        final CompiledNode<V>[] children = node.children();
1166
        final V[] values = node.orderedValues();
1167
        final int[] counts = node.orderedCounts();
1168
1169 2 1. updateNodeFingerprint : negated conditional → SURVIVED
2. updateNodeFingerprint : removed call to org/egothor/stemmer/FrequencyTrie::updateInt → SURVIVED
        updateInt(messageDigest, node.acceptsRemainingInput() ? 1 : 0);
1170 1 1. updateNodeFingerprint : removed call to org/egothor/stemmer/FrequencyTrie::updateInt → SURVIVED
        updateInt(messageDigest, edgeLabels.length);
1171
        for (char edgeLabel : edgeLabels) {
1172 1 1. updateNodeFingerprint : removed call to org/egothor/stemmer/FrequencyTrie::updateInt → SURVIVED
            updateInt(messageDigest, edgeLabel);
1173
        }
1174
        for (CompiledNode<V> child : children) {
1175
            final Integer childNodeId = nodeIds.get(child);
1176 1 1. updateNodeFingerprint : negated conditional → KILLED
            if (childNodeId == null) {
1177
                throw new IllegalStateException("Missing child node identifier during trie fingerprinting.");
1178
            }
1179 1 1. updateNodeFingerprint : removed call to org/egothor/stemmer/FrequencyTrie::updateInt → SURVIVED
            updateInt(messageDigest, childNodeId);
1180
        }
1181
1182 1 1. updateNodeFingerprint : removed call to org/egothor/stemmer/FrequencyTrie::updateInt → SURVIVED
        updateInt(messageDigest, values.length);
1183
        for (V value : values) {
1184 1 1. updateNodeFingerprint : removed call to org/egothor/stemmer/FrequencyTrie::updateUtf8 → SURVIVED
            updateUtf8(messageDigest, String.valueOf(value));
1185
        }
1186
        for (int count : counts) {
1187 1 1. updateNodeFingerprint : removed call to org/egothor/stemmer/FrequencyTrie::updateInt → KILLED
            updateInt(messageDigest, count);
1188
        }
1189
    }
1190
1191
    private static void updateUtf8(final MessageDigest messageDigest, final String value) {
1192
        final byte[] encoded = value.getBytes(StandardCharsets.UTF_8);
1193 1 1. updateUtf8 : removed call to org/egothor/stemmer/FrequencyTrie::updateInt → SURVIVED
        updateInt(messageDigest, encoded.length);
1194 1 1. updateUtf8 : removed call to java/security/MessageDigest::update → SURVIVED
        messageDigest.update(encoded);
1195
    }
1196
1197
    private static void updateInt(final MessageDigest messageDigest, final int value) {
1198 2 1. updateInt : removed call to java/security/MessageDigest::update → SURVIVED
2. updateInt : Replaced Unsigned Shift Right with Shift Left → SURVIVED
        messageDigest.update((byte) (value >>> 24));
1199 2 1. updateInt : removed call to java/security/MessageDigest::update → SURVIVED
2. updateInt : Replaced Unsigned Shift Right with Shift Left → SURVIVED
        messageDigest.update((byte) (value >>> 16));
1200 2 1. updateInt : removed call to java/security/MessageDigest::update → SURVIVED
2. updateInt : Replaced Unsigned Shift Right with Shift Left → SURVIVED
        messageDigest.update((byte) (value >>> 8));
1201 1 1. updateInt : removed call to java/security/MessageDigest::update → KILLED
        messageDigest.update((byte) value);
1202
    }
1203
1204
    private static String toLowerHex(final byte[] digest) {
1205 1 1. toLowerHex : Replaced integer multiplication with division → SURVIVED
        final StringBuilder builder = new StringBuilder(digest.length * 2);
1206
        for (byte item : digest) {
1207 3 1. toLowerHex : Replaced Unsigned Shift Right with Shift Left → KILLED
2. toLowerHex : Replaced bitwise AND with OR → KILLED
3. toLowerHex : Replaced bitwise AND with OR → KILLED
            builder.append(Character.forDigit((item >>> 4) & 0x0F, 16)).append(Character.forDigit(item & 0x0F, 16));
1208
        }
1209 1 1. toLowerHex : replaced return value with "" for org/egothor/stemmer/FrequencyTrie::toLowerHex → KILLED
        return builder.toString();
1210
    }
1211
1212
    /**
1213
     * Internal helper that materializes serialized trie data.
1214
     *
1215
     * <p>
1216
     * Moving reader complexity into this helper keeps the public-facing class from
1217
     * accumulating excessive class-level cyclomatic complexity while preserving the
1218
     * same binary compatibility contract.
1219
     * </p>
1220
     */
1221
    private static final class CompiledTrieReader {
1222
1223
        private static <V> FrequencyTrie<V> read(final InputStream inputStream, final IntFunction<V[]> arrayFactory,
1224
                final MetadataValueStreamReader<V> valueReader, final int maxExpandedIndex) throws IOException {
1225
            Objects.requireNonNull(inputStream, "inputStream");
1226
            Objects.requireNonNull(arrayFactory, "arrayFactory");
1227
            Objects.requireNonNull(valueReader, "valueReader");
1228 2 1. read : changed conditional boundary → KILLED
2. read : negated conditional → KILLED
            if (maxExpandedIndex < -1) {
1229
                throw new IllegalArgumentException("maxExpandedIndex must be >= -1.");
1230
            }
1231
1232
            final DataInputStream dataInput = wrapInputStream(inputStream);
1233
            final int magic = dataInput.readInt();
1234 1 1. read : negated conditional → KILLED
            if (magic != STREAM_MAGIC) {
1235
                throw new IOException("Unsupported trie stream header: " + Integer.toHexString(magic));
1236
            }
1237
1238
            final int version = dataInput.readInt();
1239 4 1. read : negated conditional → KILLED
2. read : changed conditional boundary → KILLED
3. read : changed conditional boundary → KILLED
4. read : negated conditional → KILLED
            if (version < MIN_STREAM_VERSION || version > STREAM_VERSION) {
1240
                throw new IOException("Unsupported trie stream version: " + version);
1241
            }
1242
1243
            final int nodeCount = dataInput.readInt();
1244 2 1. read : changed conditional boundary → SURVIVED
2. read : negated conditional → KILLED
            if (nodeCount < 0) {
1245
                throw new IOException("Negative node count: " + nodeCount);
1246
            }
1247
1248
            final int rootNodeId = dataInput.readInt();
1249 4 1. read : changed conditional boundary → KILLED
2. read : changed conditional boundary → KILLED
3. read : negated conditional → KILLED
4. read : negated conditional → KILLED
            if (rootNodeId < 0 || rootNodeId >= nodeCount) {
1250
                throw new IOException("Invalid root node id: " + rootNodeId);
1251
            }
1252
1253
            final TrieMetadata sourceMetadata = readMetadata(dataInput, version);
1254 2 1. read : negated conditional → KILLED
2. read : changed conditional boundary → KILLED
            final V[] valueTable = version >= VALUE_TABLE_VERSION
1255
                    ? readValueTable(dataInput, arrayFactory, valueReader, sourceMetadata)
1256
                    : null;
1257 2 1. read : negated conditional → KILLED
2. read : changed conditional boundary → KILLED
            final int effectiveMaxExpandedIndex = maxExpandedIndex >= 0 ? maxExpandedIndex : DEFAULT_MAX_EXPANDED_INDEX;
1258
            final CompiledNode<V>[] nodes = readNodes(dataInput, arrayFactory, valueReader, sourceMetadata, valueTable,
1259
                    nodeCount, effectiveMaxExpandedIndex, version);
1260
            final CompiledNode<V> rootNode = nodes[rootNodeId];
1261
1262
            if (LOGGER.isLoggable(Level.FINE)) {
1263
                LOGGER.log(Level.FINE, "Read compiled trie with {0} canonical nodes.", nodeCount);
1264
            }
1265
1266 1 1. read : replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::read → KILLED
            return new FrequencyTrie<>(arrayFactory, rootNode, sourceMetadata);
1267
        }
1268
1269
        /**
1270
         * Reads the temporary stream-local value table.
1271
         *
1272
         * <p>
1273
         * Each serialized value is decoded exactly once. The returned array is used
1274
         * only while materializing node value arrays and is not retained by the
1275
         * resulting trie.
1276
         * </p>
1277
         *
1278
         * @param dataInput    input stream
1279
         * @param arrayFactory typed-array factory
1280
         * @param valueReader  metadata-aware value reader
1281
         * @param metadata     parsed trie metadata
1282
         * @param <V>          value type
1283
         * @return decoded values in table-index order
1284
         * @throws IOException if the count is negative or value decoding fails
1285
         */
1286
        private static <V> V[] readValueTable(final DataInputStream dataInput, final IntFunction<V[]> arrayFactory,
1287
                final MetadataValueStreamReader<V> valueReader, final TrieMetadata metadata) throws IOException {
1288
            final int distinctValueCount = dataInput.readInt();
1289 2 1. readValueTable : changed conditional boundary → SURVIVED
2. readValueTable : negated conditional → KILLED
            if (distinctValueCount < 0) {
1290
                throw new IOException("Negative distinct value count: " + distinctValueCount);
1291
            }
1292
1293
            final V[] valueTable = arrayFactory.apply(distinctValueCount);
1294 2 1. readValueTable : negated conditional → KILLED
2. readValueTable : changed conditional boundary → KILLED
            for (int valueIndex = 0; valueIndex < distinctValueCount; valueIndex++) {
1295
                valueTable[valueIndex] = valueReader.read(dataInput, metadata);
1296
            }
1297 1 1. readValueTable : replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::readValueTable → KILLED
            return valueTable;
1298
        }
1299
1300
        private static DataInputStream wrapInputStream(final InputStream inputStream) {
1301 2 1. wrapInputStream : negated conditional → KILLED
2. wrapInputStream : replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::wrapInputStream → KILLED
            return inputStream instanceof DataInputStream ? (DataInputStream) inputStream
1302
                    : new DataInputStream(inputStream);
1303
        }
1304
1305
        private static TrieMetadata readMetadata(final DataInputStream dataInput, final int version)
1306
                throws IOException {
1307 2 1. readMetadata : negated conditional → KILLED
2. readMetadata : changed conditional boundary → KILLED
            if (version >= TEXT_METADATA_VERSION) {
1308 1 1. readMetadata : replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::readMetadata → KILLED
                return readTextMetadata(dataInput, version);
1309
            }
1310
1311
            final WordTraversalDirection traversalDirection = readTraversalDirection(dataInput, version);
1312 2 1. readMetadata : negated conditional → KILLED
2. readMetadata : changed conditional boundary → KILLED
            if (version < REDUCTION_VERSION) {
1313 1 1. readMetadata : replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::readMetadata → KILLED
                return TrieMetadata.legacy(version, traversalDirection);
1314
            }
1315
1316
            final ReductionSettings reductionSettings = readReductionSettings(dataInput);
1317
            final DiacriticProcessingMode diacriticProcessingMode = readEnumByOrdinal(dataInput,
1318
                    DiacriticProcessingMode.values(), "diacritic processing mode");
1319 2 1. readMetadata : negated conditional → KILLED
2. readMetadata : changed conditional boundary → KILLED
            final CaseProcessingMode caseProcessingMode = version >= CASE_VERSION ? readCaseProcessingMode(dataInput)
1320
                    : CaseProcessingMode.LOWERCASE_WITH_LOCALE_ROOT;
1321 1 1. readMetadata : replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::readMetadata → KILLED
            return new TrieMetadata(version, traversalDirection, reductionSettings, diacriticProcessingMode,
1322
                    caseProcessingMode);
1323
        }
1324
1325
        private static TrieMetadata readTextMetadata(final DataInputStream dataInput, final int version)
1326
                throws IOException {
1327
            try {
1328 1 1. readTextMetadata : replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::readTextMetadata → KILLED
                return TrieMetadata.fromTextBlock(version, dataInput.readUTF());
1329
            } catch (IllegalArgumentException exception) {
1330
                throw new IOException("Invalid metadata block.", exception);
1331
            }
1332
        }
1333
1334
        private static WordTraversalDirection readTraversalDirection(final DataInputStream dataInput, final int version)
1335
                throws IOException {
1336 2 1. readTraversalDirection : negated conditional → KILLED
2. readTraversalDirection : changed conditional boundary → KILLED
            if (version < TRAVERSAL_VERSION) {
1337 1 1. readTraversalDirection : replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::readTraversalDirection → KILLED
                return WordTraversalDirection.BACKWARD;
1338
            }
1339 1 1. readTraversalDirection : replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::readTraversalDirection → KILLED
            return readEnumByOrdinal(dataInput, WordTraversalDirection.values(), "traversal direction");
1340
        }
1341
1342
        private static ReductionSettings readReductionSettings(final DataInputStream dataInput) throws IOException {
1343
            final ReductionMode reductionMode = readEnumByOrdinal(dataInput, ReductionMode.values(), "reduction mode");
1344
            final int dominantWinnerMinPercent = dataInput.readInt();
1345
            final int dominantWinnerOverSecondRatio = dataInput.readInt(); // NOPMD
1346 1 1. readReductionSettings : replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::readReductionSettings → KILLED
            return new ReductionSettings(reductionMode, dominantWinnerMinPercent, dominantWinnerOverSecondRatio);
1347
        }
1348
1349
        private static CaseProcessingMode readCaseProcessingMode(final DataInputStream dataInput) throws IOException {
1350 1 1. readCaseProcessingMode : replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::readCaseProcessingMode → KILLED
            return readEnumByOrdinal(dataInput, CaseProcessingMode.values(), "case processing mode");
1351
        }
1352
1353
        private static <E extends Enum<E>> E readEnumByOrdinal(final DataInputStream dataInput, final E[] values,
1354
                final String name) throws IOException {
1355
            final int ordinal = dataInput.readInt();
1356 4 1. readEnumByOrdinal : changed conditional boundary → SURVIVED
2. readEnumByOrdinal : negated conditional → KILLED
3. readEnumByOrdinal : changed conditional boundary → KILLED
4. readEnumByOrdinal : negated conditional → KILLED
            if (ordinal < 0 || ordinal >= values.length) {
1357
                throw new IOException("Invalid " + name + " ordinal: " + ordinal);
1358
            }
1359 1 1. readEnumByOrdinal : replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::readEnumByOrdinal → KILLED
            return values[ordinal];
1360
        }
1361
1362
        private static <V> CompiledNode<V>[] readNodes(final DataInputStream dataInput,
1363
                final IntFunction<V[]> arrayFactory, final MetadataValueStreamReader<V> valueReader,
1364
                final TrieMetadata metadata, final V[] valueTable, final int nodeCount, final int maxExpandedIndex,
1365
                final int version) throws IOException {
1366
            final char[][] edgeLabelsByNode = new char[nodeCount][];
1367
            final int[][] childNodeIdsByNode = new int[nodeCount][];
1368
            @SuppressWarnings("unchecked")
1369
            final V[][] orderedValuesByNode = (V[][]) new Object[nodeCount][];
1370
            final int[][] orderedCountsByNode = new int[nodeCount][];
1371
            final boolean[] acceptsRemainingInputByNode = new boolean[nodeCount];
1372
1373 2 1. readNodes : changed conditional boundary → KILLED
2. readNodes : negated conditional → KILLED
            for (int nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++) {
1374 2 1. readNodes : negated conditional → KILLED
2. readNodes : changed conditional boundary → KILLED
                if (version >= ACCEPTING_NODE_VERSION) {
1375
                    acceptsRemainingInputByNode[nodeIndex] = dataInput.readBoolean();
1376
                }
1377
1378
                final int edgeCount = dataInput.readInt();
1379 2 1. readNodes : negated conditional → KILLED
2. readNodes : changed conditional boundary → KILLED
                if (edgeCount < 0) {
1380
                    throw new IOException("Negative edge count at node " + nodeIndex + ": " + edgeCount);
1381
                }
1382
1383
                edgeLabelsByNode[nodeIndex] = new char[edgeCount];
1384
                childNodeIdsByNode[nodeIndex] = new int[edgeCount];
1385
1386 2 1. readNodes : negated conditional → KILLED
2. readNodes : changed conditional boundary → KILLED
                for (int edgeIndex = 0; edgeIndex < edgeCount; edgeIndex++) {
1387
                    edgeLabelsByNode[nodeIndex][edgeIndex] = dataInput.readChar();
1388
                    childNodeIdsByNode[nodeIndex][edgeIndex] = dataInput.readInt();
1389
                }
1390
1391 1 1. readNodes : removed call to org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::validateSerializedEdges → KILLED
                validateSerializedEdges(nodeIndex, edgeLabelsByNode[nodeIndex]);
1392
1393
                final int valueCount = dataInput.readInt();
1394 2 1. readNodes : changed conditional boundary → KILLED
2. readNodes : negated conditional → KILLED
                if (valueCount < 0) {
1395
                    throw new IOException("Negative value count at node " + nodeIndex + ": " + valueCount);
1396
                }
1397
                // An accepting node may also carry child edges: under LookupMode.FIRST
1398
                // the accept short-circuits descent (children are inert), while
1399
                // LookupMode.LAST/ALL follow the deeper edges. Such nodes arise when a
1400
                // custom pair is added through a contracted generalization (see
1401
                // FrequencyTrieBuilders / the Python TrieBuilder).
1402 2 1. readNodes : negated conditional → KILLED
2. readNodes : negated conditional → KILLED
                if (acceptsRemainingInputByNode[nodeIndex] && valueCount == 0) {
1403
                    throw new IOException("Accepting node " + nodeIndex + " must store at least one value.");
1404
                }
1405
1406
                orderedValuesByNode[nodeIndex] = arrayFactory.apply(valueCount);
1407
                orderedCountsByNode[nodeIndex] = new int[valueCount];
1408
1409 2 1. readNodes : changed conditional boundary → KILLED
2. readNodes : negated conditional → KILLED
                for (int valueIndex = 0; valueIndex < valueCount; valueIndex++) {
1410 2 1. readNodes : negated conditional → KILLED
2. readNodes : changed conditional boundary → KILLED
                    if (version >= VALUE_TABLE_VERSION) {
1411
                        final int valueTableIndex = dataInput.readInt();
1412 4 1. readNodes : changed conditional boundary → KILLED
2. readNodes : changed conditional boundary → KILLED
3. readNodes : negated conditional → KILLED
4. readNodes : negated conditional → KILLED
                        if (valueTableIndex < 0 || valueTableIndex >= valueTable.length) {
1413
                            throw new IOException("Invalid value table index at node " + nodeIndex + ", local value "
1414
                                    + valueIndex + ": " + valueTableIndex + "; table size is " + valueTable.length
1415
                                    + '.');
1416
                        }
1417
                        orderedValuesByNode[nodeIndex][valueIndex] = valueTable[valueTableIndex];
1418
                    } else {
1419
                        orderedValuesByNode[nodeIndex][valueIndex] = valueReader.read(dataInput, metadata);
1420
                    }
1421
                    orderedCountsByNode[nodeIndex][valueIndex] = dataInput.readInt();
1422 2 1. readNodes : negated conditional → KILLED
2. readNodes : changed conditional boundary → KILLED
                    if (orderedCountsByNode[nodeIndex][valueIndex] <= 0) {
1423
                        throw new IOException("Non-positive stored count at node " + nodeIndex + ", value index "
1424
                                + valueIndex + ": " + orderedCountsByNode[nodeIndex][valueIndex]);
1425
                    }
1426
                }
1427
            }
1428
1429
            @SuppressWarnings("unchecked")
1430
            final CompiledNode<V>[] nodes = new CompiledNode[nodeCount];
1431
            final boolean[] inProgress = new boolean[nodeCount];
1432
1433 2 1. readNodes : changed conditional boundary → KILLED
2. readNodes : negated conditional → KILLED
            for (int nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++) {
1434
                nodes[nodeIndex] = resolveNode(nodeIndex, edgeLabelsByNode, childNodeIdsByNode, orderedValuesByNode,
1435
                        orderedCountsByNode, acceptsRemainingInputByNode, nodes, inProgress, maxExpandedIndex);
1436
            }
1437
1438 1 1. readNodes : replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::readNodes → KILLED
            return nodes;
1439
        }
1440
1441
        private static <V> CompiledNode<V> resolveNode(final int nodeIndex, final char[][] edgeLabelsByNode,
1442
                final int[][] childNodeIdsByNode, final V[][] orderedValuesByNode, final int[][] orderedCountsByNode,
1443
                final boolean[] acceptsRemainingInputByNode, final CompiledNode<V>[] nodes,
1444
                final boolean[] inProgress, final int maxExpandedIndex) throws IOException {
1445
            final CompiledNode<V> cachedNode = nodes[nodeIndex];
1446 1 1. resolveNode : negated conditional → KILLED
            if (cachedNode != null) {
1447 1 1. resolveNode : replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::resolveNode → KILLED
                return cachedNode;
1448
            }
1449
1450 1 1. resolveNode : negated conditional → KILLED
            if (inProgress[nodeIndex]) {
1451
                throw new IOException(
1452
                        "Invalid serialized node graph: cyclic reference detected at node " + nodeIndex + '.');
1453
            }
1454
            inProgress[nodeIndex] = true;
1455
            try {
1456
                final char[] edgeLabels = edgeLabelsByNode[nodeIndex];
1457
                final int[] childNodeIds = childNodeIdsByNode[nodeIndex];
1458
                final int edgeCount = childNodeIds.length;
1459
                @SuppressWarnings("unchecked")
1460
                final CompiledNode<V>[] children = new CompiledNode[edgeCount];
1461
1462 2 1. resolveNode : negated conditional → KILLED
2. resolveNode : changed conditional boundary → KILLED
                for (int edgeIndex = 0; edgeIndex < edgeCount; edgeIndex++) {
1463
                    final int childNodeId = childNodeIds[edgeIndex];
1464 4 1. resolveNode : changed conditional boundary → SURVIVED
2. resolveNode : negated conditional → KILLED
3. resolveNode : changed conditional boundary → KILLED
4. resolveNode : negated conditional → KILLED
                    if (childNodeId < 0 || childNodeId >= edgeLabelsByNode.length) {
1465
                        throw new IOException("Invalid child node id at node " + nodeIndex + ", edge index " + edgeIndex
1466
                                + ": " + childNodeId);
1467
                    }
1468
                    children[edgeIndex] = resolveNode(childNodeId, edgeLabelsByNode, childNodeIdsByNode,
1469
                            orderedValuesByNode, orderedCountsByNode, acceptsRemainingInputByNode, nodes, inProgress,
1470
                            maxExpandedIndex);
1471
                }
1472
1473
                final CompiledNode<V> node = new CompiledNode<>(edgeLabels, children, orderedValuesByNode[nodeIndex],
1474
                        acceptsRemainingInputByNode[nodeIndex], maxExpandedIndex, orderedCountsByNode[nodeIndex]);
1475
                nodes[nodeIndex] = node;
1476 1 1. resolveNode : replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::resolveNode → KILLED
                return node;
1477
            } finally {
1478
                inProgress[nodeIndex] = false;
1479
            }
1480
        }
1481
1482
        private static void validateSerializedEdges(final int nodeIndex, final char... edgeLabels) throws IOException {
1483 2 1. validateSerializedEdges : changed conditional boundary → KILLED
2. validateSerializedEdges : negated conditional → KILLED
            for (int edgeIndex = 1; edgeIndex < edgeLabels.length; edgeIndex++) {
1484 3 1. validateSerializedEdges : changed conditional boundary → SURVIVED
2. validateSerializedEdges : negated conditional → KILLED
3. validateSerializedEdges : Replaced integer subtraction with addition → KILLED
                if (edgeLabels[edgeIndex - 1] >= edgeLabels[edgeIndex]) {
1485 1 1. validateSerializedEdges : Replaced integer subtraction with addition → KILLED
                    throw new IOException(
1486
                            "Edge labels must be strictly ascending at node " + nodeIndex + ", edge index " + edgeIndex
1487
                                    + ": '" + edgeLabels[edgeIndex - 1] + "' then '" + edgeLabels[edgeIndex] + "'.");
1488
                }
1489
            }
1490
        }
1491
    }
1492
1493
    /**
1494
     * Locates the compiled node for the supplied key.
1495
     *
1496
     * @param key already-normalized key to resolve
1497
     * @return compiled node, or {@code null} if the path does not exist
1498
     */
1499
    private CompiledNode<V> findNode(final String key) {
1500
        CompiledNode<V> current = this.root;
1501 1 1. findNode : negated conditional → KILLED
        if (this.lookupTraversalDirection == WordTraversalDirection.BACKWARD) {
1502 3 1. findNode : changed conditional boundary → KILLED
2. findNode : Replaced integer subtraction with addition → KILLED
3. findNode : negated conditional → KILLED
            for (int traversalOffset = key.length() - 1; traversalOffset >= 0; traversalOffset--) {
1503 1 1. findNode : negated conditional → KILLED
                if (current.acceptsRemainingInput()) {
1504 1 1. findNode : replaced return value with null for org/egothor/stemmer/FrequencyTrie::findNode → KILLED
                    return current;
1505
                }
1506
                current = current.findChild(key.charAt(traversalOffset));
1507 1 1. findNode : negated conditional → KILLED
                if (current == null) {
1508
                    return null;
1509
                }
1510
            }
1511 1 1. findNode : replaced return value with null for org/egothor/stemmer/FrequencyTrie::findNode → KILLED
            return current;
1512
        }
1513
1514 2 1. findNode : negated conditional → KILLED
2. findNode : changed conditional boundary → KILLED
        for (int traversalOffset = 0; traversalOffset < key.length(); traversalOffset++) {
1515 1 1. findNode : negated conditional → KILLED
            if (current.acceptsRemainingInput()) {
1516 1 1. findNode : replaced return value with null for org/egothor/stemmer/FrequencyTrie::findNode → KILLED
                return current;
1517
            }
1518
            current = current.findChild(key.charAt(traversalOffset));
1519 1 1. findNode : negated conditional → KILLED
            if (current == null) {
1520
                return null;
1521
            }
1522
        }
1523 1 1. findNode : replaced return value with null for org/egothor/stemmer/FrequencyTrie::findNode → KILLED
        return current;
1524
    }
1525
1526
    /**
1527
     * Locates the compiled node for the supplied key.
1528
     *
1529
     * @param key already-normalized key to resolve
1530
     * @return compiled node, or {@code null} if the path does not exist
1531
     */
1532
    private CompiledNode<V> findNode(final CharSequence key) {
1533
        CompiledNode<V> current = this.root;
1534 1 1. findNode : negated conditional → KILLED
        if (this.lookupTraversalDirection == WordTraversalDirection.BACKWARD) {
1535 3 1. findNode : changed conditional boundary → KILLED
2. findNode : Replaced integer subtraction with addition → KILLED
3. findNode : negated conditional → KILLED
            for (int traversalOffset = key.length() - 1; traversalOffset >= 0; traversalOffset--) {
1536 1 1. findNode : negated conditional → KILLED
                if (current.acceptsRemainingInput()) {
1537 1 1. findNode : replaced return value with null for org/egothor/stemmer/FrequencyTrie::findNode → NO_COVERAGE
                    return current;
1538
                }
1539
                current = current.findChild(key.charAt(traversalOffset));
1540 1 1. findNode : negated conditional → KILLED
                if (current == null) {
1541
                    return null;
1542
                }
1543
            }
1544 1 1. findNode : replaced return value with null for org/egothor/stemmer/FrequencyTrie::findNode → KILLED
            return current;
1545
        }
1546
1547 2 1. findNode : negated conditional → NO_COVERAGE
2. findNode : changed conditional boundary → NO_COVERAGE
        for (int traversalOffset = 0; traversalOffset < key.length(); traversalOffset++) {
1548 1 1. findNode : negated conditional → NO_COVERAGE
            if (current.acceptsRemainingInput()) {
1549 1 1. findNode : replaced return value with null for org/egothor/stemmer/FrequencyTrie::findNode → NO_COVERAGE
                return current;
1550
            }
1551
            current = current.findChild(key.charAt(traversalOffset));
1552 1 1. findNode : negated conditional → NO_COVERAGE
            if (current == null) {
1553
                return null;
1554
            }
1555
        }
1556 1 1. findNode : replaced return value with null for org/egothor/stemmer/FrequencyTrie::findNode → NO_COVERAGE
        return current;
1557
    }
1558
1559
    /**
1560
     * Locates the compiled node for the supplied key slice.
1561
     *
1562
     * @param key    already-normalized key storage
1563
     * @param offset first character offset
1564
     * @param length number of characters to read
1565
     * @return compiled node, or {@code null} if the path does not exist
1566
     */
1567
    private CompiledNode<V> findNode(final char[] key, final int offset, final int length) {
1568
        CompiledNode<V> current = this.root;
1569 1 1. findNode : negated conditional → KILLED
        if (this.lookupTraversalDirection == WordTraversalDirection.BACKWARD) {
1570 4 1. findNode : Replaced integer subtraction with addition → KILLED
2. findNode : Replaced integer addition with subtraction → KILLED
3. findNode : negated conditional → KILLED
4. findNode : changed conditional boundary → KILLED
            for (int traversalOffset = offset + length - 1; traversalOffset >= offset; traversalOffset--) {
1571 1 1. findNode : negated conditional → KILLED
                if (current.acceptsRemainingInput()) {
1572 1 1. findNode : replaced return value with null for org/egothor/stemmer/FrequencyTrie::findNode → NO_COVERAGE
                    return current;
1573
                }
1574
                current = current.findChild(key[traversalOffset]);
1575 1 1. findNode : negated conditional → KILLED
                if (current == null) {
1576
                    return null;
1577
                }
1578
            }
1579 1 1. findNode : replaced return value with null for org/egothor/stemmer/FrequencyTrie::findNode → KILLED
            return current;
1580
        }
1581
1582 1 1. findNode : Replaced integer addition with subtraction → NO_COVERAGE
        final int endExclusive = offset + length;
1583 2 1. findNode : negated conditional → NO_COVERAGE
2. findNode : changed conditional boundary → NO_COVERAGE
        for (int traversalOffset = offset; traversalOffset < endExclusive; traversalOffset++) {
1584 1 1. findNode : negated conditional → NO_COVERAGE
            if (current.acceptsRemainingInput()) {
1585 1 1. findNode : replaced return value with null for org/egothor/stemmer/FrequencyTrie::findNode → NO_COVERAGE
                return current;
1586
            }
1587
            current = current.findChild(key[traversalOffset]);
1588 1 1. findNode : negated conditional → NO_COVERAGE
            if (current == null) {
1589
                return null;
1590
            }
1591
        }
1592 1 1. findNode : replaced return value with null for org/egothor/stemmer/FrequencyTrie::findNode → NO_COVERAGE
        return current;
1593
    }
1594
1595
    /**
1596
     * Visits node-local values without allocating result containers.
1597
     *
1598
     * @param node       resolved node, or {@code null}
1599
     * @param sink       value sink
1600
     * @param maxResults maximum values to visit
1601
     * @return number of visited values
1602
     */
1603
    private int visitNode(final CompiledNode<V> node, final EntrySink<? super V> sink, final int maxResults) {
1604 1 1. visitNode : negated conditional → KILLED
        if (node == null) {
1605
            return 0;
1606
        }
1607
1608
        final V[] orderedValues = node.orderedValues();
1609
        final int valueCount = Math.min(orderedValues.length, maxResults);
1610 1 1. visitNode : negated conditional → KILLED
        if (valueCount == 0) {
1611
            return 0;
1612
        }
1613
1614
        final int[] orderedCounts = node.orderedCounts();
1615
        int visited = 0;
1616 2 1. visitNode : changed conditional boundary → KILLED
2. visitNode : negated conditional → KILLED
        for (int rank = 0; rank < valueCount; rank++) {
1617 1 1. visitNode : Changed increment from 1 to -1 → KILLED
            visited++;
1618 1 1. visitNode : negated conditional → KILLED
            if (!sink.accept(orderedValues[rank], orderedCounts[rank], rank)) {
1619
                break;
1620
            }
1621
        }
1622 1 1. visitNode : replaced int return with 0 for org/egothor/stemmer/FrequencyTrie::visitNode → KILLED
        return visited;
1623
    }
1624
1625
    /**
1626
     * Validates visitor maximum result count.
1627
     *
1628
     * @param maxResults maximum result count
1629
     */
1630
    private static void validateMaxResults(final int maxResults) {
1631 2 1. validateMaxResults : negated conditional → KILLED
2. validateMaxResults : changed conditional boundary → KILLED
        if (maxResults < 0) {
1632
            throw new IllegalArgumentException("maxResults must be non-negative.");
1633
        }
1634
    }
1635
1636
    /**
1637
     * Applies lookup-time case normalization according to persisted metadata.
1638
     *
1639
     * @param key lookup key
1640
     * @return normalized key for trie traversal
1641
     */
1642
    private String normalizeLookupKey(final String key) {
1643 1 1. normalizeLookupKey : replaced return value with "" for org/egothor/stemmer/FrequencyTrie::normalizeLookupKey → KILLED
        return normalizeLookupKey((CharSequence) key).toString();
1644
    }
1645
1646
    /**
1647
     * Applies lookup-time normalization according to persisted metadata.
1648
     *
1649
     * @param key lookup key
1650
     * @return normalized key for trie traversal
1651
     */
1652
    private CharSequence normalizeLookupKey(final CharSequence key) {
1653 2 1. normalizeLookupKey : negated conditional → SURVIVED
2. normalizeLookupKey : negated conditional → KILLED
        if (!this.lowercasesLookupKeys && !this.removeDiacritics) {
1654 1 1. normalizeLookupKey : replaced return value with null for org/egothor/stemmer/FrequencyTrie::normalizeLookupKey → KILLED
            return key;
1655
        }
1656
1657
        String normalized = key.toString();
1658 1 1. normalizeLookupKey : negated conditional → KILLED
        if (this.lowercasesLookupKeys) {
1659
            normalized = normalized.toLowerCase(Locale.ROOT);
1660
        }
1661 1 1. normalizeLookupKey : negated conditional → KILLED
        if (this.removeDiacritics) {
1662
            normalized = DiacriticStripper.strip(normalized);
1663 1 1. normalizeLookupKey : negated conditional → KILLED
        } else if (this.metadata.diacriticProcessingMode() == DiacriticProcessingMode.AS_IS_AND_STRIPPED_FALLBACK) {
1664
            throw new UnsupportedOperationException(
1665
                    "Diacritic processing mode AS_IS_AND_STRIPPED_FALLBACK is not supported yet.");
1666
        }
1667
1668 1 1. normalizeLookupKey : replaced return value with null for org/egothor/stemmer/FrequencyTrie::normalizeLookupKey → KILLED
        return normalized;
1669
    }
1670
1671
    /**
1672
     * Builder of {@link FrequencyTrie}.
1673
     *
1674
     * <p>
1675
     * The builder is intentionally mutable and optimized for repeated
1676
     * {@link #put(String, Object)} calls. The final trie is created by
1677
     * {@link #build()}, which performs bottom-up subtree reduction and converts the
1678
     * structure to a compact immutable representation optimized for read
1679
     * operations.
1680
     * </p>
1681
     *
1682
     * <p>
1683
     * A builder is mutable and not thread-safe. Callers must externally serialize
1684
     * all access. Each call to {@link #build()} creates an immutable snapshot;
1685
     * subsequent builder updates do not affect previously built tries.
1686
     * </p>
1687
     *
1688
     * <p>
1689
     * Update operations compose in invocation order. For example, the following
1690
     * sequence starts with two candidates, promotes one without losing the other,
1691
     * removes the former candidate, and then replaces the remaining value:
1692
     * </p>
1693
     *
1694
     * <pre>{@code
1695
     * FrequencyTrie.Builder<String> builder =
1696
     *         new FrequencyTrie.Builder<>(String[]::new,
1697
     *                 ReductionMode.MERGE_SUBTREES_WITH_EQUIVALENT_RANKED_GET_ALL_RESULTS);
1698
     * builder.put("token", "legacy", 3)
1699
     *         .put("token", "alternate")
1700
     *         .putDominant("token", "alternate")
1701
     *         .remove("token", "legacy")
1702
     *         .set("token", "curated");
1703
     *
1704
     * FrequencyTrie<String> snapshot = builder.build();
1705
     * // snapshot.get("token") returns "curated" and getAll contains no alternatives.
1706
     * }</pre>
1707
     *
1708
     * <p>
1709
     * {@link #putIfAbsent(String, Object)} is a no-op while a node has any local
1710
     * value. {@link #remove(String)} and {@link #remove(String, Object)} are no-ops
1711
     * for missing targets. An empty key is valid and addresses the root node. When
1712
     * modifying a reconstructed contracted trie, exact-node removal does not remove
1713
     * a shorter generalization; add a specific rule and use {@link LookupMode#LAST}
1714
     * when the more-specific rule must win.
1715
     * </p>
1716
     *
1717
     * @param <V> value type
1718
     */
1719
    public static final class Builder<V> {
1720
1721
        /**
1722
         * Logger of this class.
1723
         */
1724
        private static final Logger LOGGER = Logger.getLogger(Builder.class.getName());
1725
1726
        /**
1727
         * Factory used to create typed arrays.
1728
         */
1729
        private final IntFunction<V[]> arrayFactory;
1730
1731
        /**
1732
         * Reduction configuration.
1733
         */
1734
        private final ReductionSettings reductionSettings;
1735
1736
        /**
1737
         * Logical key traversal direction used by this builder.
1738
         */
1739
        private final WordTraversalDirection traversalDirection;
1740
1741
        /**
1742
         * Dictionary case processing mode associated with this builder.
1743
         */
1744
        private final CaseProcessingMode caseProcessingMode;
1745
1746
        /**
1747
         * Dictionary diacritic processing mode associated with this builder.
1748
         */
1749
        private final DiacriticProcessingMode diacriticProcessingMode;
1750
1751
        /**
1752
         * Dense edge lookup span threshold.
1753
         * <p>
1754
         * This value controls a speed/memory trade-off during freezing: dense child
1755
         * lookup tables are allocated only for nodes whose child labels fit in this
1756
         * span.
1757
         * </p>
1758
         */
1759
        private final int maxExpandedIndex;
1760
1761
        /**
1762
         * Mutable root node.
1763
         */
1764
        private final MutableNode<V> root;
1765
1766
        /**
1767
         * Source compiled-node identity for each mutable node expanded by
1768
         * {@link FrequencyTrieBuilders#copyOf}.
1769
         */
1770
        private final Map<MutableNode<V>, Object> compiledSources;
1771
1772
        /**
1773
         * Unique reduction discriminator for each locally modified reconstructed
1774
         * node.
1775
         */
1776
        private final Map<MutableNode<V>, Object> mergeDiscriminators;
1777
1778
        /**
1779
         * Creates a new builder with the provided settings.
1780
         *
1781
         * <p>
1782
         * This constructor preserves the historical Egothor behavior and therefore
1783
         * traverses logical keys from their end toward their beginning.
1784
         * </p>
1785
         *
1786
         * @param arrayFactory      array factory
1787
         * @param reductionSettings reduction configuration
1788
         * @throws NullPointerException if any argument is {@code null}
1789
         */
1790
        public Builder(final IntFunction<V[]> arrayFactory, final ReductionSettings reductionSettings) {
1791
            this(arrayFactory, reductionSettings, WordTraversalDirection.BACKWARD);
1792
        }
1793
1794
        /**
1795
         * Creates a new builder with the provided settings and explicit traversal
1796
         * direction.
1797
         *
1798
         * @param arrayFactory       array factory
1799
         * @param reductionSettings  reduction configuration
1800
         * @param traversalDirection logical key traversal direction
1801
         * @throws NullPointerException if any argument is {@code null}
1802
         */
1803
        public Builder(final IntFunction<V[]> arrayFactory, final ReductionSettings reductionSettings,
1804
                final WordTraversalDirection traversalDirection) {
1805
            this(arrayFactory, reductionSettings, traversalDirection, CaseProcessingMode.LOWERCASE_WITH_LOCALE_ROOT);
1806
        }
1807
1808
        /**
1809
         * Creates a new builder with the provided settings, explicit traversal
1810
         * direction, and explicit case processing mode.
1811
         *
1812
         * @param arrayFactory       array factory
1813
         * @param reductionSettings  reduction configuration
1814
         * @param traversalDirection logical key traversal direction
1815
         * @param caseProcessingMode dictionary case processing mode
1816
         * @throws NullPointerException if any argument is {@code null}
1817
         */
1818
        public Builder(final IntFunction<V[]> arrayFactory, final ReductionSettings reductionSettings,
1819
                final WordTraversalDirection traversalDirection, final CaseProcessingMode caseProcessingMode) {
1820
            this(arrayFactory, reductionSettings, traversalDirection, caseProcessingMode,
1821
                    DiacriticProcessingMode.AS_IS);
1822
        }
1823
1824
        /**
1825
         * Creates a new builder with the provided settings, explicit traversal
1826
         * direction, explicit case processing mode, and explicit diacritic processing
1827
         * mode.
1828
         *
1829
         * @param arrayFactory            array factory
1830
         * @param reductionSettings       reduction configuration
1831
         * @param traversalDirection      logical key traversal direction
1832
         * @param caseProcessingMode      dictionary case processing mode
1833
         * @param diacriticProcessingMode dictionary diacritic processing mode
1834
         * @throws NullPointerException if any argument is {@code null}
1835
         */
1836
        public Builder(final IntFunction<V[]> arrayFactory, final ReductionSettings reductionSettings,
1837
                final WordTraversalDirection traversalDirection, final CaseProcessingMode caseProcessingMode,
1838
                final DiacriticProcessingMode diacriticProcessingMode) {
1839
            this(arrayFactory, reductionSettings, traversalDirection, caseProcessingMode, diacriticProcessingMode,
1840
                    CompiledNode.DEFAULT_MAX_EXPANDED_INDEX);
1841
        }
1842
1843
        /**
1844
         * Creates a new builder with the provided settings, explicit traversal
1845
         * direction, explicit case processing mode, explicit diacritic processing mode,
1846
         * and an explicit dense child lookup threshold.
1847
         *
1848
         * @param arrayFactory            array factory
1849
         * @param reductionSettings       reduction configuration
1850
         * @param traversalDirection      logical key traversal direction
1851
         * @param caseProcessingMode      dictionary case processing mode
1852
         * @param diacriticProcessingMode dictionary diacritic processing mode
1853
         * @param maxExpandedIndex        dense lookup span override; zero disables
1854
         *                                dense lookup. Larger values increase direct
1855
         *                                indexing opportunities while potentially
1856
         *                                increasing materialization memory in nodes
1857
         *                                whose edge label span is within the limit.
1858
         * @throws NullPointerException if any argument is {@code null}
1859
         * @throws IllegalArgumentException if {@code maxExpandedIndex} is negative
1860
         */
1861
        public Builder(final IntFunction<V[]> arrayFactory, final ReductionSettings reductionSettings,
1862
                final WordTraversalDirection traversalDirection, final CaseProcessingMode caseProcessingMode,
1863
                final DiacriticProcessingMode diacriticProcessingMode, final int maxExpandedIndex) {
1864
            this.arrayFactory = Objects.requireNonNull(arrayFactory, "arrayFactory");
1865
            this.reductionSettings = Objects.requireNonNull(reductionSettings, "reductionSettings");
1866
            this.traversalDirection = Objects.requireNonNull(traversalDirection, "traversalDirection");
1867
            this.caseProcessingMode = Objects.requireNonNull(caseProcessingMode, "caseProcessingMode");
1868
            this.diacriticProcessingMode = Objects.requireNonNull(diacriticProcessingMode, "diacriticProcessingMode");
1869 2 1. <init> : changed conditional boundary → SURVIVED
2. <init> : negated conditional → KILLED
            if (maxExpandedIndex < 0) {
1870
                throw new IllegalArgumentException("maxExpandedIndex must be non-negative.");
1871
            }
1872
            this.maxExpandedIndex = maxExpandedIndex;
1873
            this.root = new MutableNode<>();
1874
            this.compiledSources = new IdentityHashMap<>();
1875
            this.mergeDiscriminators = new IdentityHashMap<>();
1876
        }
1877
1878
        /**
1879
         * Creates a new builder using default thresholds for the supplied reduction
1880
         * mode.
1881
         *
1882
         * <p>
1883
         * This constructor preserves the historical Egothor behavior and therefore
1884
         * traverses logical keys from their end toward their beginning.
1885
         * </p>
1886
         *
1887
         * @param arrayFactory  array factory
1888
         * @param reductionMode reduction mode
1889
         * @throws NullPointerException if any argument is {@code null}
1890
         */
1891
        public Builder(final IntFunction<V[]> arrayFactory, final ReductionMode reductionMode) {
1892
            this(arrayFactory, ReductionSettings.withDefaults(reductionMode), WordTraversalDirection.BACKWARD);
1893
        }
1894
1895
        /**
1896
         * Creates a new builder using default thresholds for the supplied reduction
1897
         * mode and explicit traversal direction.
1898
         *
1899
         * @param arrayFactory       array factory
1900
         * @param reductionMode      reduction mode
1901
         * @param traversalDirection logical key traversal direction
1902
         * @throws NullPointerException if any argument is {@code null}
1903
         */
1904
        public Builder(final IntFunction<V[]> arrayFactory, final ReductionMode reductionMode,
1905
                final WordTraversalDirection traversalDirection) {
1906
            this(arrayFactory, ReductionSettings.withDefaults(reductionMode), traversalDirection);
1907
        }
1908
1909
        /**
1910
         * Stores a value for the supplied key and increments its local frequency.
1911
         *
1912
         * <p>
1913
         * Values are stored at the node addressed by the full key. Since trie values
1914
         * may also appear on internal nodes, an empty key is valid and stores a value
1915
         * directly at the root.
1916
         *
1917
         * @param key   key
1918
         * @param value value
1919
         * @return this builder
1920
         * @throws NullPointerException if {@code key} or {@code value} is {@code null}
1921
         */
1922
        public Builder<V> put(final String key, final V value) {
1923 1 1. put : replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::put → KILLED
            return put(key, value, 1);
1924
        }
1925
1926
        /**
1927
         * Builds a compiled read-only trie.
1928
         *
1929
         * @return compiled trie
1930
         * @throws ArithmeticException if reduction would aggregate a local value
1931
         *                             count beyond {@link Integer#MAX_VALUE}
1932
         */
1933
        public FrequencyTrie<V> build() {
1934
            if (LOGGER.isLoggable(Level.FINE)) {
1935
                LOGGER.log(Level.FINE, "Starting trie compilation with reduction mode {0}.",
1936
                        this.reductionSettings.reductionMode());
1937
            }
1938
1939
            final ReductionContext<V> reductionContext = new ReductionContext<>(this.reductionSettings);
1940
            final ReducedNode<V> reducedRoot = reduce(this.root, reductionContext);
1941
            final CompiledNode<V> compiledRoot = freeze(reducedRoot, new IdentityHashMap<>());
1942
1943
            if (LOGGER.isLoggable(Level.FINE)) {
1944
                LOGGER.log(Level.FINE, "Trie compilation finished. Canonical node count: {0}.",
1945
                        reductionContext.canonicalNodeCount());
1946
            }
1947
1948
            final TrieMetadata metadata = TrieMetadata.forCompilation(this.traversalDirection, this.reductionSettings,
1949
                    this.diacriticProcessingMode, this.caseProcessingMode);
1950 1 1. build : replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::build → KILLED
            return new FrequencyTrie<>(this.arrayFactory, compiledRoot, metadata);
1951
        }
1952
1953
        /**
1954
         * Stores a value for the supplied key and increments its local frequency by the
1955
         * specified positive count.
1956
         *
1957
         * <p>
1958
         * Values are stored at the node addressed by the full key. Since trie values
1959
         * may also appear on internal nodes, an empty key is valid and stores a value
1960
         * directly at the root.
1961
         *
1962
         * <p>
1963
         * This method is functionally equivalent to calling
1964
         * {@link #put(String, Object)} repeatedly {@code count} times, but it avoids
1965
         * unnecessary repeated map updates and is therefore preferable for bulk
1966
         * reconstruction from compiled tries or other aggregated sources.
1967
         *
1968
         * @param key   key
1969
         * @param value value
1970
         * @param count positive frequency increment
1971
         * @return this builder
1972
         * @throws NullPointerException     if {@code key} or {@code value} is
1973
         *                                  {@code null}
1974
         * @throws IllegalArgumentException if {@code count} is less than {@code 1}
1975
         * @throws ArithmeticException      if the accumulated count would exceed
1976
         *                                  {@link Integer#MAX_VALUE}
1977
         */
1978
        public Builder<V> put(final String key, final V value, final int count) {
1979
            Objects.requireNonNull(key, ARG_KEY);
1980
            Objects.requireNonNull(value, "value");
1981
1982 2 1. put : changed conditional boundary → KILLED
2. put : negated conditional → KILLED
            if (count < 1) { // NOPMD
1983
                throw new IllegalArgumentException("count must be at least 1.");
1984
            }
1985
1986
            final MutableNode<V> current = navigateToNode(normalizeDictionaryKey(key));
1987
            final Integer previous = current.valueCounts().get(value);
1988 1 1. put : negated conditional → KILLED
            final int updatedCount = previous == null ? count : Math.addExact(previous, count);
1989 1 1. put : removed call to org/egothor/stemmer/FrequencyTrie$Builder::markModified → KILLED
            markModified(current);
1990
            current.valueCounts().put(value, updatedCount);
1991 1 1. put : replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::put → KILLED
            return this;
1992
        }
1993
1994
        /**
1995
         * Makes {@code value} the dominant (highest-frequency) local value at the node
1996
         * addressed by {@code key}, keeping any other values as lower-ranked
1997
         * alternatives.
1998
         *
1999
         * <p>
2000
         * The value's count is set to one more than the highest count among the other
2001
         * values at that node, so {@link FrequencyTrie#get(String)} returns it while
2002
         * {@link FrequencyTrie#getAll(String)} still lists the alternatives. Use this
2003
         * to override a rule for one key without discarding the prior candidates.
2004
         *
2005
         * <p>
2006
         * This differs from {@link #put(String, Object, int)}, which adds a raw
2007
         * frequency that may or may not dominate, and from {@link #set(String, Object)},
2008
         * which discards the other values entirely.
2009
         *
2010
         * @param key   key
2011
         * @param value value to make dominant
2012
         * @return this builder
2013
         * @throws NullPointerException if {@code key} or {@code value} is {@code null}
2014
         * @throws ArithmeticException  if another value already has a count of
2015
         *                              {@link Integer#MAX_VALUE}
2016
         */
2017
        public Builder<V> putDominant(final String key, final V value) {
2018
            Objects.requireNonNull(key, ARG_KEY);
2019
            Objects.requireNonNull(value, "value");
2020
2021
            final MutableNode<V> node = navigateToNode(normalizeDictionaryKey(key));
2022
            final Map<V, Integer> counts = node.valueCounts();
2023
            int maxOther = 0;
2024
            for (final Map.Entry<V, Integer> entry : counts.entrySet()) {
2025 1 1. putDominant : negated conditional → KILLED
                if (!entry.getKey().equals(value)) {
2026
                    maxOther = Math.max(maxOther, entry.getValue());
2027
                }
2028
            }
2029 1 1. putDominant : negated conditional → KILLED
            if (maxOther == Integer.MAX_VALUE) {
2030
                throw new ArithmeticException(
2031
                        "Cannot make value dominant because another value already has Integer.MAX_VALUE count.");
2032
            }
2033 1 1. putDominant : removed call to org/egothor/stemmer/FrequencyTrie$Builder::markModified → SURVIVED
            markModified(node);
2034 1 1. putDominant : Replaced integer addition with subtraction → KILLED
            counts.put(value, maxOther + 1);
2035 1 1. putDominant : replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::putDominant → KILLED
            return this;
2036
        }
2037
2038
        /**
2039
         * Replaces every local value at the node addressed by {@code key} with the
2040
         * single {@code value} (count 1), making it the sole and therefore dominant
2041
         * value there. Any prior values at that node are discarded.
2042
         *
2043
         * <p>
2044
         * This differs from {@link #put(String, Object, int)} (which accumulates) and
2045
         * {@link #putDominant(String, Object)} (which dominates but keeps the
2046
         * alternatives).
2047
         *
2048
         * @param key   key
2049
         * @param value replacement value
2050
         * @return this builder
2051
         * @throws NullPointerException if {@code key} or {@code value} is {@code null}
2052
         */
2053
        public Builder<V> set(final String key, final V value) {
2054
            Objects.requireNonNull(key, ARG_KEY);
2055
            Objects.requireNonNull(value, "value");
2056
2057
            final MutableNode<V> node = navigateToNode(normalizeDictionaryKey(key));
2058 1 1. set : removed call to org/egothor/stemmer/FrequencyTrie$Builder::markModified → SURVIVED
            markModified(node);
2059
            final Map<V, Integer> counts = node.valueCounts();
2060 1 1. set : removed call to java/util/Map::clear → KILLED
            counts.clear();
2061
            counts.put(value, 1);
2062 1 1. set : replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::set → KILLED
            return this;
2063
        }
2064
2065
        /**
2066
         * Stores {@code value} (count 1) at the node addressed by {@code key} only when
2067
         * that node currently has no local value; otherwise the builder is unchanged.
2068
         * Use this to fill gaps without overwriting curated entries.
2069
         *
2070
         * @param key   key
2071
         * @param value value to store when absent
2072
         * @return this builder
2073
         * @throws NullPointerException if {@code key} or {@code value} is {@code null}
2074
         */
2075
        public Builder<V> putIfAbsent(final String key, final V value) {
2076
            Objects.requireNonNull(key, ARG_KEY);
2077
            Objects.requireNonNull(value, "value");
2078
2079
            final MutableNode<V> node = navigateToNode(normalizeDictionaryKey(key));
2080
            final Map<V, Integer> counts = node.valueCounts();
2081 1 1. putIfAbsent : negated conditional → KILLED
            if (counts.isEmpty()) {
2082 1 1. putIfAbsent : removed call to org/egothor/stemmer/FrequencyTrie$Builder::markModified → SURVIVED
                markModified(node);
2083
                counts.put(value, 1);
2084
            }
2085 1 1. putIfAbsent : replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::putIfAbsent → KILLED
            return this;
2086
        }
2087
2088
        /**
2089
         * Removes every local value at the node addressed by {@code key}. The node
2090
         * structure is retained but stores no value, so {@link FrequencyTrie#get(String)}
2091
         * returns {@code null} for that exact key. A key whose path does not exist is a
2092
         * no-op; the trie structure is not pruned.
2093
         *
2094
         * <p>
2095
         * <strong>This targets the exact node for {@code key} only.</strong> A key that
2096
         * resolves through a shorter contracted generalization does not have a value at
2097
         * its own full-length node — reduction may have collapsed its rule into a higher
2098
         * suffix node — so removing the full key does not change how it resolves. To
2099
         * suppress or re-map such a key, add a specific rule instead
2100
         * ({@link #set(String, Object)} or {@link #putDominant(String, Object)}) and read
2101
         * with {@link LookupMode#LAST}, or remove the shorter generalization key (which
2102
         * affects every key it covers).
2103
         *
2104
         * @param key key whose local values are removed
2105
         * @return this builder
2106
         * @throws NullPointerException if {@code key} is {@code null}
2107
         */
2108
        public Builder<V> remove(final String key) {
2109
            Objects.requireNonNull(key, ARG_KEY);
2110
2111
            final MutableNode<V> node = findMutableNode(normalizeDictionaryKey(key));
2112 2 1. remove : negated conditional → KILLED
2. remove : negated conditional → KILLED
            if (node != null && !node.valueCounts().isEmpty()) {
2113 1 1. remove : removed call to org/egothor/stemmer/FrequencyTrie$Builder::markModified → SURVIVED
                markModified(node);
2114 1 1. remove : removed call to java/util/Map::clear → KILLED
                node.valueCounts().clear();
2115 1 1. remove : removed call to org/egothor/stemmer/trie/MutableNode::clearAcceptsRemainingInput → KILLED
                node.clearAcceptsRemainingInput();
2116
            }
2117 1 1. remove : replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::remove → KILLED
            return this;
2118
        }
2119
2120
        /**
2121
         * Removes a single {@code value} from the node addressed by {@code key},
2122
         * keeping any other values there. A missing key or value is a no-op.
2123
         *
2124
         * @param key   key
2125
         * @param value value to remove
2126
         * @return this builder
2127
         * @throws NullPointerException if {@code key} or {@code value} is {@code null}
2128
         */
2129
        public Builder<V> remove(final String key, final V value) {
2130
            Objects.requireNonNull(key, ARG_KEY);
2131
            Objects.requireNonNull(value, "value");
2132
2133
            final MutableNode<V> node = findMutableNode(normalizeDictionaryKey(key));
2134 2 1. remove : negated conditional → KILLED
2. remove : negated conditional → KILLED
            if (node != null && node.valueCounts().containsKey(value)) {
2135 1 1. remove : removed call to org/egothor/stemmer/FrequencyTrie$Builder::markModified → SURVIVED
                markModified(node);
2136
                node.valueCounts().remove(value);
2137 1 1. remove : negated conditional → SURVIVED
                if (node.valueCounts().isEmpty()) {
2138 1 1. remove : removed call to org/egothor/stemmer/trie/MutableNode::clearAcceptsRemainingInput → NO_COVERAGE
                    node.clearAcceptsRemainingInput();
2139
                }
2140
            }
2141 1 1. remove : replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::remove → KILLED
            return this;
2142
        }
2143
2144
        /**
2145
         * Navigates to the mutable node addressed by an already-normalized key without
2146
         * creating missing nodes.
2147
         *
2148
         * @param normalizedKey already-normalized key
2149
         * @return addressed node, or {@code null} if the path does not exist
2150
         */
2151
        private MutableNode<V> findMutableNode(final String normalizedKey) {
2152
            MutableNode<V> current = this.root;
2153 2 1. findMutableNode : changed conditional boundary → KILLED
2. findMutableNode : negated conditional → KILLED
            for (int traversalOffset = 0; traversalOffset < normalizedKey.length(); traversalOffset++) {
2154
                final Character edge = normalizedKey
2155
                        .charAt(this.traversalDirection.logicalIndex(normalizedKey.length(), traversalOffset));
2156
                current = current.children().get(edge);
2157 1 1. findMutableNode : negated conditional → KILLED
                if (current == null) {
2158
                    return null;
2159
                }
2160
            }
2161 1 1. findMutableNode : replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::findMutableNode → KILLED
            return current;
2162
        }
2163
2164
        /**
2165
         * Navigates to (creating as needed) the mutable node addressed by an
2166
         * already-normalized key, consuming characters in traversal-direction order.
2167
         *
2168
         * @param normalizedKey already-normalized dictionary key
2169
         * @return addressed mutable node
2170
         */
2171
        private MutableNode<V> navigateToNode(final String normalizedKey) {
2172
            MutableNode<V> current = this.root;
2173 2 1. navigateToNode : changed conditional boundary → KILLED
2. navigateToNode : negated conditional → KILLED
            for (int traversalOffset = 0; traversalOffset < normalizedKey.length(); traversalOffset++) {
2174
                final Character edge = normalizedKey
2175
                        .charAt(this.traversalDirection.logicalIndex(normalizedKey.length(), traversalOffset));
2176
                MutableNode<V> child = current.children().get(edge);
2177 1 1. navigateToNode : negated conditional → KILLED
                if (child == null) {
2178
                    child = new MutableNode<>(); // NOPMD
2179
                    current.children().put(edge, child);
2180
                }
2181
                current = child;
2182
            }
2183 1 1. navigateToNode : replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::navigateToNode → KILLED
            return current;
2184
        }
2185
2186
        /**
2187
         * Marks the node addressed by {@code key} as accepting any remaining input,
2188
         * so reduction preserves a compiled trie's contracted generalization. The
2189
         * node can later acquire children when a more-specific override is added.
2190
         * Used by {@link FrequencyTrieBuilders#copyOf} when reconstructing a writable
2191
         * builder from a compiled trie.
2192
         *
2193
         * @param key logical key of the accepting node
2194
         * @return this builder
2195
         * @throws NullPointerException if {@code key} is {@code null}
2196
         */
2197
        /* default */ Builder<V> markAcceptsRemainingInput(final String key) {
2198
            Objects.requireNonNull(key, ARG_KEY);
2199 1 1. markAcceptsRemainingInput : removed call to org/egothor/stemmer/trie/MutableNode::markAcceptsRemainingInput → KILLED
            navigateToNode(normalizeDictionaryKey(key)).markAcceptsRemainingInput();
2200 1 1. markAcceptsRemainingInput : replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::markAcceptsRemainingInput → SURVIVED
            return this;
2201
        }
2202
2203
        /**
2204
         * Associates the mutable node at {@code key} with its source compiled DAG
2205
         * node.
2206
         *
2207
         * <p>
2208
         * The association prevents already-aggregated counts from being multiplied
2209
         * when shared compiled nodes are expanded to logical paths and then reduced
2210
         * again. This reconstruction-only operation must be invoked after copying
2211
         * the source node's values and accepting state.
2212
         * </p>
2213
         *
2214
         * @param key            logical key of the expanded node
2215
         * @param sourceIdentity non-null source compiled-node identity
2216
         * @return this builder
2217
         * @throws NullPointerException if either argument is {@code null}
2218
         * @throws IllegalStateException if the addressed mutable node was already
2219
         *                               associated with a different compiled node
2220
         */
2221
        /* default */ Builder<V> recordCompiledSource(final String key, final Object sourceIdentity) {
2222
            Objects.requireNonNull(key, ARG_KEY);
2223
            Objects.requireNonNull(sourceIdentity, "sourceIdentity");
2224
            final MutableNode<V> node = navigateToNode(normalizeDictionaryKey(key));
2225
            final Object previous = this.compiledSources.putIfAbsent(node, sourceIdentity);
2226 2 1. recordCompiledSource : negated conditional → NO_COVERAGE
2. recordCompiledSource : negated conditional → KILLED
            if (previous != null && previous != sourceIdentity) { // NOPMD - graph identity is intentional
2227
                throw new IllegalStateException(
2228
                        "Mutable node is already associated with another compiled source node.");
2229
            }
2230 1 1. recordCompiledSource : replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::recordCompiledSource → SURVIVED
            return this;
2231
        }
2232
2233
        /**
2234
         * Establishes a copy-on-write reduction boundary after a reconstructed
2235
         * node's local state changes.
2236
         *
2237
         * <p>
2238
         * Nodes created by ordinary insertion need no discriminator and continue to
2239
         * use the configured semantic subtree reduction. For a reconstructed node,
2240
         * the first successful update installs one stable identity token so it can
2241
         * no longer merge back into unchanged logical paths expanded from the same
2242
         * compiled DAG node.
2243
         * </p>
2244
         *
2245
         * @param node successfully modified mutable node
2246
         */
2247
        private void markModified(final MutableNode<V> node) {
2248 1 1. markModified : negated conditional → KILLED
            if (this.compiledSources.containsKey(node)) {
2249 1 1. lambda$markModified$0 : replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::lambda$markModified$0 → KILLED
                this.mergeDiscriminators.computeIfAbsent(node, ignored -> new Object());
2250
            }
2251
        }
2252
2253
        /**
2254
         * Applies build-time dictionary-key normalization according to the builder
2255
         * configuration.
2256
         *
2257
         * @param key dictionary key
2258
         * @return normalized key for trie insertion
2259
         */
2260
        private String normalizeDictionaryKey(final String key) {
2261
            String normalized = key;
2262
2263 1 1. normalizeDictionaryKey : negated conditional → KILLED
            if (this.caseProcessingMode == CaseProcessingMode.LOWERCASE_WITH_LOCALE_ROOT) {
2264
                normalized = normalized.toLowerCase(Locale.ROOT);
2265
            }
2266
2267 1 1. normalizeDictionaryKey : negated conditional → KILLED
            if (this.diacriticProcessingMode == DiacriticProcessingMode.REMOVE) {
2268
                normalized = DiacriticStripper.strip(normalized);
2269 1 1. normalizeDictionaryKey : negated conditional → KILLED
            } else if (this.diacriticProcessingMode == DiacriticProcessingMode.AS_IS_AND_STRIPPED_FALLBACK) {
2270
                throw new UnsupportedOperationException(
2271
                        "Diacritic processing mode AS_IS_AND_STRIPPED_FALLBACK is not supported yet.");
2272
            }
2273
2274 1 1. normalizeDictionaryKey : replaced return value with "" for org/egothor/stemmer/FrequencyTrie$Builder::normalizeDictionaryKey → KILLED
            return normalized;
2275
        }
2276
2277
        /**
2278
         * Returns the number of mutable build-time nodes currently reachable from the
2279
         * builder root.
2280
         *
2281
         * <p>
2282
         * This metric is intended mainly for diagnostics and tests that compare the
2283
         * unreduced build-time structure with the final reduced compiled trie.
2284
         *
2285
         * @return number of mutable build-time nodes
2286
         */
2287
        /* default */ int buildTimeSize() {
2288 1 1. buildTimeSize : replaced int return with 0 for org/egothor/stemmer/FrequencyTrie$Builder::buildTimeSize → KILLED
            return countMutableNodes(this.root);
2289
        }
2290
2291
        /**
2292
         * Returns the logical key traversal direction used by this builder.
2293
         *
2294
         * @return logical key traversal direction
2295
         */
2296
        /* default */ WordTraversalDirection traversalDirection() {
2297 1 1. traversalDirection : replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::traversalDirection → NO_COVERAGE
            return this.traversalDirection;
2298
        }
2299
2300
        /**
2301
         * Counts mutable nodes recursively.
2302
         *
2303
         * @param node current node
2304
         * @return reachable mutable node count
2305
         */
2306
        private int countMutableNodes(final MutableNode<V> node) {
2307
            int count = 1;
2308
            for (MutableNode<V> child : node.children().values()) {
2309 1 1. countMutableNodes : Replaced integer addition with subtraction → KILLED
                count += countMutableNodes(child);
2310
            }
2311 1 1. countMutableNodes : replaced int return with 0 for org/egothor/stemmer/FrequencyTrie$Builder::countMutableNodes → KILLED
            return count;
2312
        }
2313
2314
        /**
2315
         * Reduces a mutable node to a canonical reduced node.
2316
         *
2317
         * @param source  source mutable node
2318
         * @param context reduction context
2319
         * @return canonical reduced node
2320
         */
2321
        private ReducedNode<V> reduce(final MutableNode<V> source, final ReductionContext<V> context) {
2322
            Map<Character, ReducedNode<V>> reducedChildren = new LinkedHashMap<>();
2323
2324
            for (Map.Entry<Character, MutableNode<V>> childEntry : source.children().entrySet()) {
2325
                final ReducedNode<V> reducedChild = reduce(childEntry.getValue(), context);
2326
                reducedChildren.put(childEntry.getKey(), reducedChild);
2327
            }
2328
2329
            Map<V, Integer> localCounts = copyCounts(source.valueCounts());
2330
            boolean acceptsRemainingInput = false;
2331 1 1. reduce : negated conditional → KILLED
            if (source.acceptsRemainingInput()) {
2332
                // Preserved from a compiled trie's contracted accepting leaf (see
2333
                // FrequencyTrieBuilders.copyOf). The generalization is kept verbatim;
2334
                // any child edges added afterwards are retained so LookupMode.LAST/ALL
2335
                // can follow a more specific override under the same accepting node.
2336
                acceptsRemainingInput = true;
2337 1 1. reduce : negated conditional → KILLED
            } else if (context.settings().contractUniformSubtrees()) {
2338
                final Map<V, Integer> contractedCounts = contractUniformSubtree(localCounts, reducedChildren);
2339 1 1. reduce : negated conditional → KILLED
                if (!contractedCounts.isEmpty()) {
2340
                    localCounts = contractedCounts;
2341
                    reducedChildren = Collections.emptyMap();
2342
                    acceptsRemainingInput = true;
2343
                }
2344
            }
2345
2346
            final LocalValueSummary<V> localSummary = LocalValueSummary.of(localCounts, this.arrayFactory);
2347
            final ReductionSignature<V> signature = ReductionSignature.create(localSummary, reducedChildren,
2348
                    context.settings(), acceptsRemainingInput, this.mergeDiscriminators.get(source));
2349
            final Object compiledSource = this.compiledSources.get(source);
2350
2351
            ReducedNode<V> canonical = context.lookup(signature);
2352 1 1. reduce : negated conditional → KILLED
            if (canonical == null) {
2353
                canonical = new ReducedNode<>(signature, localCounts, reducedChildren, acceptsRemainingInput);
2354 1 1. reduce : removed call to org/egothor/stemmer/trie/ReductionContext::register → KILLED
                context.register(signature, canonical);
2355 1 1. reduce : negated conditional → KILLED
                if (compiledSource != null) {
2356
                    context.recordCompiledSourceContribution(canonical, compiledSource);
2357
                }
2358 1 1. reduce : replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::reduce → KILLED
                return canonical;
2359
            }
2360
2361 1 1. reduce : negated conditional → KILLED
            if (compiledSource == null
2362 1 1. reduce : negated conditional → KILLED
                    || context.recordCompiledSourceContribution(canonical, compiledSource)) {
2363 1 1. reduce : removed call to org/egothor/stemmer/trie/ReducedNode::mergeLocalCounts → KILLED
                canonical.mergeLocalCounts(localCounts);
2364
            }
2365 1 1. reduce : removed call to org/egothor/stemmer/trie/ReducedNode::mergeChildren → SURVIVED
            canonical.mergeChildren(reducedChildren);
2366
2367 1 1. reduce : replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::reduce → KILLED
            return canonical;
2368
        }
2369
2370
        /**
2371
         * Returns aggregated local counts when the supplied internal subtree contains
2372
         * one uniform value, otherwise {@code null}.
2373
         *
2374
         * @param localCounts     local counts at the current node
2375
         * @param reducedChildren already reduced children
2376
         * @return single-value aggregate for a uniform non-leaf subtree, otherwise an
2377
         *         empty map
2378
         */
2379
        private Map<V, Integer> contractUniformSubtree(final Map<V, Integer> localCounts,
2380
                final Map<Character, ReducedNode<V>> reducedChildren) {
2381 1 1. contractUniformSubtree : negated conditional → KILLED
            if (reducedChildren.isEmpty()) {
2382
                return Collections.emptyMap();
2383
            }
2384
2385
            V uniformValue = null;
2386
            boolean valueSeen = false;
2387
2388 1 1. contractUniformSubtree : negated conditional → KILLED
            if (!localCounts.isEmpty()) {
2389 1 1. contractUniformSubtree : negated conditional → SURVIVED
                if (localCounts.size() != SINGLE_VALUE_COUNT) {
2390
                    return Collections.emptyMap();
2391
                }
2392
                final Map.Entry<V, Integer> localEntry = localCounts.entrySet().iterator().next();
2393
                uniformValue = localEntry.getKey();
2394
                valueSeen = true;
2395
            }
2396
2397
            for (ReducedNode<V> child : reducedChildren.values()) {
2398 1 1. contractUniformSubtree : negated conditional → KILLED
                if (!isSingleValueLeaf(child)) {
2399
                    return Collections.emptyMap();
2400
                }
2401
                final Map.Entry<V, Integer> childEntry = child.localCounts().entrySet().iterator().next();
2402 2 1. contractUniformSubtree : negated conditional → KILLED
2. contractUniformSubtree : negated conditional → KILLED
                if (valueSeen && !Objects.equals(uniformValue, childEntry.getKey())) {
2403
                    return Collections.emptyMap();
2404
                }
2405
                uniformValue = childEntry.getKey();
2406
                valueSeen = true;
2407
            }
2408
2409 1 1. contractUniformSubtree : negated conditional → KILLED
            if (!valueSeen) {
2410
                return Collections.emptyMap();
2411
            }
2412
2413
            final Map<V, Integer> contractedCounts = new LinkedHashMap<>(SINGLE_VALUE_COUNT);
2414
            contractedCounts.put(uniformValue, SINGLE_VALUE_COUNT);
2415 1 1. contractUniformSubtree : replaced return value with Collections.emptyMap for org/egothor/stemmer/FrequencyTrie$Builder::contractUniformSubtree → KILLED
            return contractedCounts;
2416
        }
2417
2418
        /**
2419
         * Returns whether the reduced node is a leaf with exactly one stored value.
2420
         *
2421
         * @param node node to inspect
2422
         * @return {@code true} when the node can participate in uniform contraction
2423
         */
2424
        private boolean isSingleValueLeaf(final ReducedNode<V> node) {
2425 3 1. isSingleValueLeaf : negated conditional → KILLED
2. isSingleValueLeaf : replaced boolean return with true for org/egothor/stemmer/FrequencyTrie$Builder::isSingleValueLeaf → KILLED
3. isSingleValueLeaf : negated conditional → KILLED
            return node.children().isEmpty() && node.localCounts().size() == SINGLE_VALUE_COUNT;
2426
        }
2427
2428
        /**
2429
         * Freezes a reduced node into an immutable compiled node.
2430
         *
2431
         * @param reducedNode reduced node
2432
         * @param cache       already frozen nodes
2433
         * @return immutable compiled node
2434
         */
2435
        private CompiledNode<V> freeze(final ReducedNode<V> reducedNode,
2436
                final Map<ReducedNode<V>, CompiledNode<V>> cache) {
2437
            final CompiledNode<V> existing = cache.get(reducedNode);
2438 1 1. freeze : negated conditional → KILLED
            if (existing != null) {
2439 1 1. freeze : replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::freeze → KILLED
                return existing;
2440
            }
2441
2442
            final LocalValueSummary<V> localSummary = LocalValueSummary.of(reducedNode.localCounts(),
2443
                    this.arrayFactory);
2444
2445
            final List<Map.Entry<Character, ReducedNode<V>>> childEntries = new ArrayList<>(
2446
                    reducedNode.children().entrySet());
2447 1 1. freeze : removed call to java/util/List::sort → KILLED
            childEntries.sort(Map.Entry.comparingByKey());
2448
2449
            final char[] edges = new char[childEntries.size()];
2450
            @SuppressWarnings("unchecked")
2451
            final CompiledNode<V>[] childNodes = new CompiledNode[childEntries.size()];
2452
2453 2 1. freeze : changed conditional boundary → KILLED
2. freeze : negated conditional → KILLED
            for (int index = 0; index < childEntries.size(); index++) {
2454
                final Map.Entry<Character, ReducedNode<V>> entry = childEntries.get(index);
2455
                edges[index] = entry.getKey();
2456
                childNodes[index] = freeze(entry.getValue(), cache);
2457
            }
2458
2459
            final CompiledNode<V> frozen = new CompiledNode<>(edges, childNodes, localSummary.orderedValues(),
2460
                    reducedNode.acceptsRemainingInput(), this.maxExpandedIndex, localSummary.orderedCounts());
2461
            cache.put(reducedNode, frozen);
2462 1 1. freeze : replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::freeze → KILLED
            return frozen;
2463
        }
2464
2465
        /**
2466
         * Creates a shallow frequency copy preserving deterministic insertion order of
2467
         * first occurrence.
2468
         *
2469
         * @param source source counts
2470
         * @return copied counts
2471
         */
2472
        private Map<V, Integer> copyCounts(final Map<V, Integer> source) {
2473 1 1. copyCounts : replaced return value with Collections.emptyMap for org/egothor/stemmer/FrequencyTrie$Builder::copyCounts → KILLED
            return new LinkedHashMap<>(source);
2474
        }
2475
    }
2476
2477
    /**
2478
     * Reads one final trie value using metadata parsed from the same binary stream.
2479
     *
2480
     * <p>
2481
     * Implementations are invoked only during deserialization and are not retained
2482
     * by the resulting trie. Returned values are stored directly in final compiled
2483
     * node arrays.
2484
     * </p>
2485
     *
2486
     * @param <V> final value type
2487
     */
2488
    /* default */
2489
    @FunctionalInterface
2490
    interface MetadataValueStreamReader<V> {
2491
2492
        /**
2493
         * Reads and materializes one final value.
2494
         *
2495
         * @param dataInput source data input
2496
         * @param metadata  already parsed trie metadata
2497
         * @return final value to store directly in compiled nodes
2498
         * @throws IOException if reading or materialization fails
2499
         */
2500
        V read(DataInputStream dataInput, TrieMetadata metadata) throws IOException;
2501
    }
2502
2503
    /**
2504
     * Codec used to persist values stored in the trie.
2505
     *
2506
     * @param <V> value type
2507
     */
2508
    public interface ValueStreamCodec<V> {
2509
2510
        /**
2511
         * Writes one value to the supplied data output.
2512
         *
2513
         * @param dataOutput target data output
2514
         * @param value      value to write
2515
         * @throws IOException if writing fails
2516
         */
2517
        void write(DataOutputStream dataOutput, V value) throws IOException;
2518
2519
        /**
2520
         * Reads one value from the supplied data input.
2521
         *
2522
         * @param dataInput source data input
2523
         * @return read value
2524
         * @throws IOException if reading fails
2525
         */
2526
        V read(DataInputStream dataInput) throws IOException;
2527
    }
2528
2529
}

Mutations

263

1.1
Location : currentFormatVersion
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:currentCompiledTrieFormatVersionIsSeven()]
replaced int return with 0 for org/egothor/stemmer/FrequencyTrie::currentFormatVersion → KILLED

275

1.1
Location : usesValueTableFormat
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

2.2
Location : usesValueTableFormat
Killed by : org.egothor.stemmer.StemmerPatchTrieBinaryIOTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerPatchTrieBinaryIOTest]/[nested-class:ReadTests]/[method:shouldCompileRepeatedVersionSixInlineCommandsOnce()]
negated conditional → KILLED

3.3
Location : usesValueTableFormat
Killed by : org.egothor.stemmer.StemmerPatchTrieBinaryIOTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerPatchTrieBinaryIOTest]/[nested-class:ReadTests]/[method:shouldCompileRepeatedVersionSixInlineCommandsOnce()]
replaced boolean return with true for org/egothor/stemmer/FrequencyTrie::usesValueTableFormat → KILLED

315

1.1
Location : <init>
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:lookupKeepsCaseSensitiveBehaviorWhenMetadataIsAsIs()]
negated conditional → KILLED

316

1.1
Location : <init>
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:diacriticRemoveModeStripsDictionaryAndLookupKeys()]
negated conditional → KILLED

319

1.1
Location : <init>
Killed by : org.egothor.stemmer.FrequencyTrieLookupModeTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieLookupModeTest]/[method:lastSelectsMostSpecific()]
negated conditional → KILLED

350

1.1
Location : lookupMode
Killed by : org.egothor.stemmer.FrequencyTrieLookupModeTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieLookupModeTest]/[method:firstSelectsShallowestAccepting()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::lookupMode → KILLED

374

1.1
Location : withLookupMode
Killed by : org.egothor.stemmer.FrequencyTrieLookupModeTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieLookupModeTest]/[method:lastSelectsMostSpecific()]
negated conditional → KILLED

375

1.1
Location : withLookupMode
Killed by : org.egothor.stemmer.FrequencyTrieLookupModeTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieLookupModeTest]/[method:withLookupModeReturnsSharedView()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::withLookupMode → KILLED

377

1.1
Location : withLookupMode
Killed by : org.egothor.stemmer.FrequencyTrieLookupModeTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieLookupModeTest]/[method:lastSelectsMostSpecific()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::withLookupMode → KILLED

392

1.1
Location : fromCompiled
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:shouldPreserveUniformSubtreeContractionWhenMappingValues()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::fromCompiled → KILLED

420

1.1
Location : get
Killed by : org.egothor.stemmer.FrequencyTrieLookupModeTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieLookupModeTest]/[method:firstSelectsShallowestAccepting()]
negated conditional → KILLED

423

1.1
Location : get
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:emptyTrieReturnsNullEmptyArrayAndEmptyEntries()]
negated conditional → KILLED

427

1.1
Location : get
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:emptyKeyStoresValuesAtRootNode()]
negated conditional → KILLED

430

1.1
Location : get
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:emptyKeyStoresValuesAtRootNode()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::get → KILLED

451

1.1
Location : getNormalized
Killed by : none
negated conditional → SURVIVED
Covering tests

454

1.1
Location : getNormalized
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupSupportsCharSlicesAndMetadataAwareKeys()]
negated conditional → KILLED

458

1.1
Location : getNormalized
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupSupportsCharSlicesAndMetadataAwareKeys()]
negated conditional → KILLED

461

1.1
Location : getNormalized
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupSupportsCharSlicesAndMetadataAwareKeys()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::getNormalized → KILLED

481

1.1
Location : getNormalizedString
Killed by : none
negated conditional → SURVIVED
Covering tests

484

1.1
Location : getNormalizedString
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupSupportsCharSlicesAndMetadataAwareKeys()]
negated conditional → KILLED

488

1.1
Location : getNormalizedString
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupSupportsCharSlicesAndMetadataAwareKeys()]
negated conditional → KILLED

491

1.1
Location : getNormalizedString
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupSupportsCharSlicesAndMetadataAwareKeys()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::getNormalizedString → KILLED

527

1.1
Location : getAll
Killed by : org.egothor.stemmer.FrequencyTrieLookupModeTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieLookupModeTest]/[method:firstSelectsShallowestAccepting()]
negated conditional → KILLED

528

1.1
Location : getAll
Killed by : org.egothor.stemmer.FrequencyTrieLookupModeTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieLookupModeTest]/[method:allCollectsCandidatesMostSpecificFirst()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::getAll → KILLED

531

1.1
Location : getAll
Killed by : org.egothor.stemmer.FrequencyTrieLookupModeTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieLookupModeTest]/[method:firstSelectsShallowestAccepting()]
negated conditional → KILLED

534

1.1
Location : getAll
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:emptyTrieReturnsNullEmptyArrayAndEmptyEntries()]
negated conditional → KILLED

535

1.1
Location : getAll
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:emptyTrieReturnsNullEmptyArrayAndEmptyEntries()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::getAll → KILLED

538

1.1
Location : getAll
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:emptyKeyStoresValuesAtRootNode()]
negated conditional → KILLED

539

1.1
Location : getAll
Killed by : none
replaced return value with null for org/egothor/stemmer/FrequencyTrie::getAll → SURVIVED
Covering tests

541

1.1
Location : getAll
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:emptyKeyStoresValuesAtRootNode()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::getAll → KILLED

571

1.1
Location : getEntries
Killed by : org.egothor.stemmer.FrequencyTrieLookupModeTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieLookupModeTest]/[method:allCollectsCandidatesMostSpecificFirst()]
negated conditional → KILLED

572

1.1
Location : getEntries
Killed by : org.egothor.stemmer.FrequencyTrieLookupModeTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieLookupModeTest]/[method:allCollectsCandidatesMostSpecificFirst()]
replaced return value with Collections.emptyList for org/egothor/stemmer/FrequencyTrie::getEntries → KILLED

575

1.1
Location : getEntries
Killed by : none
negated conditional → SURVIVED
Covering tests

578

1.1
Location : getEntries
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:emptyTrieReturnsNullEmptyArrayAndEmptyEntries()]
negated conditional → KILLED

584

1.1
Location : getEntries
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:emptyKeyStoresValuesAtRootNode()]
negated conditional → KILLED

588

1.1
Location : getEntries
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:emptyKeyStoresValuesAtRootNode()]
negated conditional → KILLED

589

1.1
Location : getEntries
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromSupportsInlineValuesInStreamVersionsFiveAndSix()]
replaced return value with Collections.emptyList for org/egothor/stemmer/FrequencyTrie::getEntries → KILLED

594

1.1
Location : getEntries
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:emptyKeyStoresValuesAtRootNode()]
changed conditional boundary → KILLED

2.2
Location : getEntries
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:emptyKeyStoresValuesAtRootNode()]
negated conditional → KILLED

597

1.1
Location : getEntries
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:emptyKeyStoresValuesAtRootNode()]
replaced return value with Collections.emptyList for org/egothor/stemmer/FrequencyTrie::getEntries → KILLED

628

1.1
Location : getAllNormalized
Killed by : none
removed call to org/egothor/stemmer/FrequencyTrie::validateMaxResults → SURVIVED
Covering tests

629

1.1
Location : getAllNormalized
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupSupportsCharSlicesAndMetadataAwareKeys()]
negated conditional → KILLED

632

1.1
Location : getAllNormalized
Killed by : none
negated conditional → SURVIVED
Covering tests

633

1.1
Location : getAllNormalized
Killed by : none
replaced int return with 0 for org/egothor/stemmer/FrequencyTrie::getAllNormalized → NO_COVERAGE

637

1.1
Location : getAllNormalized
Killed by : none
negated conditional → SURVIVED
Covering tests

640

1.1
Location : getAllNormalized
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupSupportsCharSlicesAndMetadataAwareKeys()]
replaced int return with 0 for org/egothor/stemmer/FrequencyTrie::getAllNormalized → KILLED

659

1.1
Location : getAllNormalized
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupHandlesBoundaryCases()]
removed call to org/egothor/stemmer/FrequencyTrie::validateMaxResults → KILLED

660

1.1
Location : getAllNormalized
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupHonorsMaxResultsAndSinkEarlyStop()]
negated conditional → KILLED

663

1.1
Location : getAllNormalized
Killed by : none
negated conditional → SURVIVED
Covering tests

664

1.1
Location : getAllNormalized
Killed by : none
replaced int return with 0 for org/egothor/stemmer/FrequencyTrie::getAllNormalized → NO_COVERAGE

667

1.1
Location : getAllNormalized
Killed by : none
negated conditional → SURVIVED
Covering tests

670

1.1
Location : getAllNormalized
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupHonorsMaxResultsAndSinkEarlyStop()]
replaced int return with 0 for org/egothor/stemmer/FrequencyTrie::getAllNormalized → KILLED

686

1.1
Location : getFirstNormalized
Killed by : none
replaced boolean return with true for org/egothor/stemmer/FrequencyTrie::getFirstNormalized → NO_COVERAGE

2.2
Location : getFirstNormalized
Killed by : none
negated conditional → NO_COVERAGE

699

1.1
Location : getFirstNormalized
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupHandlesBoundaryCases()]
negated conditional → KILLED

2.2
Location : getFirstNormalized
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupHandlesBoundaryCases()]
replaced boolean return with true for org/egothor/stemmer/FrequencyTrie::getFirstNormalized → KILLED

720

1.1
Location : getAll
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupRejectsNullAndInvalidRangeArguments()]
removed call to org/egothor/stemmer/FrequencyTrie::validateMaxResults → KILLED

721

1.1
Location : getAll
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupSupportsCharSlicesAndMetadataAwareKeys()]
negated conditional → KILLED

725

1.1
Location : getAll
Killed by : none
negated conditional → SURVIVED
Covering tests

726

1.1
Location : getAll
Killed by : none
replaced int return with 0 for org/egothor/stemmer/FrequencyTrie::getAll → NO_COVERAGE

729

1.1
Location : getAll
Killed by : none
negated conditional → SURVIVED
Covering tests

732

1.1
Location : getAll
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupSupportsCharSlicesAndMetadataAwareKeys()]
replaced int return with 0 for org/egothor/stemmer/FrequencyTrie::getAll → KILLED

745

1.1
Location : getFirst
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupSupportsCharSlicesAndMetadataAwareKeys()]
negated conditional → KILLED

2.2
Location : getFirst
Killed by : none
replaced boolean return with true for org/egothor/stemmer/FrequencyTrie::getFirst → SURVIVED
Covering tests

759

1.1
Location : traversalDirection
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:ordinaryOperationsDoNotCalculateFingerprint()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::traversalDirection → KILLED

768

1.1
Location : metadata
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromSupportsLegacyVersionTwoMetadata()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::metadata → KILLED

795

1.1
Location : getFingerprint
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:firstGetFingerprintCalculatesAndCachesFingerprint()]
replaced return value with "" for org/egothor/stemmer/FrequencyTrie::getFingerprint → KILLED

815

1.1
Location : copyFingerprintBytes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:firstGetFingerprintCalculatesAndCachesFingerprint()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::copyFingerprintBytes → KILLED

832

1.1
Location : fingerprintBytes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:firstGetFingerprintCalculatesAndCachesFingerprint()]
removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED

835

1.1
Location : fingerprintBytes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:firstGetFingerprintCalculatesAndCachesFingerprint()]
negated conditional → KILLED

839

1.1
Location : fingerprintBytes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:firstGetFingerprintCalculatesAndCachesFingerprint()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::fingerprintBytes → KILLED

841

1.1
Location : fingerprintBytes
Killed by : none
removed call to java/util/concurrent/locks/ReentrantLock::unlock → TIMED_OUT

847

1.1
Location : computeFingerprintBytes
Killed by : none
removed call to org/egothor/stemmer/FrequencyTrie::updateUtf8 → SURVIVED
Covering tests

848

1.1
Location : computeFingerprintBytes
Killed by : none
removed call to org/egothor/stemmer/FrequencyTrie::updateInt → SURVIVED
Covering tests

849

1.1
Location : computeFingerprintBytes
Killed by : none
removed call to org/egothor/stemmer/FrequencyTrie::updateUtf8 → SURVIVED
Covering tests

853

1.1
Location : computeFingerprintBytes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:firstGetFingerprintCalculatesAndCachesFingerprint()]
removed call to org/egothor/stemmer/FrequencyTrie::assignNodeIds → KILLED

855

1.1
Location : computeFingerprintBytes
Killed by : none
removed call to org/egothor/stemmer/FrequencyTrie::updateInt → SURVIVED
Covering tests

856

1.1
Location : computeFingerprintBytes
Killed by : none
removed call to org/egothor/stemmer/FrequencyTrie::updateInt → SURVIVED
Covering tests

858

1.1
Location : computeFingerprintBytes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:firstGetFingerprintCalculatesAndCachesFingerprint()]
removed call to org/egothor/stemmer/FrequencyTrie::updateNodeFingerprint → KILLED

860

1.1
Location : computeFingerprintBytes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:firstGetFingerprintCalculatesAndCachesFingerprint()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::computeFingerprintBytes → KILLED

869

1.1
Location : root
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:shouldKeepExactLookupWhenUniformSubtreeContractionIsDisabled()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::root → KILLED

894

1.1
Location : writeTo
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:writeToAndReadFromRejectNullArguments()]
negated conditional → KILLED

902

1.1
Location : writeTo
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:writeToAndReadFromRejectNullArguments()]
removed call to org/egothor/stemmer/FrequencyTrie::assignNodeIds → KILLED

905

1.1
Location : writeTo
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenWriterEncodesEachEqualityDistinctValueOnce()]
removed call to org/egothor/stemmer/FrequencyTrie::collectDistinctValues → KILLED

911

1.1
Location : writeTo
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenStreamWritesHeaderMetadataAndValueTableInOrder()]
removed call to java/io/DataOutputStream::writeInt → KILLED

912

1.1
Location : writeTo
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenStreamWritesHeaderMetadataAndValueTableInOrder()]
removed call to java/io/DataOutputStream::writeInt → KILLED

913

1.1
Location : writeTo
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenStreamWritesHeaderMetadataAndValueTableInOrder()]
removed call to java/io/DataOutputStream::writeInt → KILLED

914

1.1
Location : writeTo
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenStreamWritesHeaderMetadataAndValueTableInOrder()]
removed call to java/io/DataOutputStream::writeInt → KILLED

915

1.1
Location : writeTo
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenStreamWritesHeaderMetadataAndValueTableInOrder()]
removed call to org/egothor/stemmer/FrequencyTrie::writeMetadata → KILLED

916

1.1
Location : writeTo
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenWriterEncodesEachEqualityDistinctValueOnce()]
removed call to org/egothor/stemmer/FrequencyTrie::writeValueTable → KILLED

918

1.1
Location : writeTo
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenReaderDecodesEachTableValueOnce()]
negated conditional → KILLED

2.2
Location : writeTo
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:writeToAndReadFromRejectNullArguments()]
changed conditional boundary → KILLED

919

1.1
Location : writeTo
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenReaderDecodesEachTableValueOnce()]
removed call to org/egothor/stemmer/FrequencyTrie::writeNode → KILLED

922

1.1
Location : writeTo
Killed by : none
removed call to java/io/DataOutputStream::flush → SURVIVED
Covering tests

942

1.1
Location : readFrom
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromSupportsLegacyVersionTwoMetadata()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::readFrom → KILLED

968

1.1
Location : readFrom
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromSupportsLegacyVersionTwoMetadata()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::readFrom → KILLED

969

1.1
Location : lambda$readFrom$0
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromSupportsInlineValuesInStreamVersionsFiveAndSix()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::lambda$readFrom$0 → KILLED

997

1.1
Location : readFromWithMetadata
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromSupportsLegacyVersionTwoMetadata()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::readFromWithMetadata → KILLED

1009

1.1
Location : writeMetadata
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenStreamWritesHeaderMetadataAndValueTableInOrder()]
removed call to java/io/DataOutputStream::writeUTF → KILLED

1026

1.1
Location : metadataForCurrentStream
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:writeToAndReadFromRejectNullArguments()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::metadataForCurrentStream → KILLED

1043

1.1
Location : size
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:ordinaryOperationsDoNotCalculateFingerprint()]
removed call to org/egothor/stemmer/FrequencyTrie::assignNodeIds → KILLED

1044

1.1
Location : size
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:ordinaryOperationsDoNotCalculateFingerprint()]
replaced int return with 0 for org/egothor/stemmer/FrequencyTrie::size → KILLED

1057

1.1
Location : assignNodeIds
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:writeToAndReadFromRejectNullArguments()]
negated conditional → KILLED

1066

1.1
Location : assignNodeIds
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:firstGetFingerprintCalculatesAndCachesFingerprint()]
removed call to org/egothor/stemmer/FrequencyTrie::assignNodeIds → KILLED

1089

1.1
Location : collectDistinctValues
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenWriterEncodesEachEqualityDistinctValueOnce()]
negated conditional → KILLED

1109

1.1
Location : writeValueTable
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenStreamWritesHeaderMetadataAndValueTableInOrder()]
removed call to java/io/DataOutputStream::writeInt → KILLED

1111

1.1
Location : writeValueTable
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenWriterEncodesEachEqualityDistinctValueOnce()]
removed call to org/egothor/stemmer/FrequencyTrie$ValueStreamCodec::write → KILLED

1128

1.1
Location : writeNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenReaderDecodesEachTableValueOnce()]
removed call to java/io/DataOutputStream::writeBoolean → KILLED

1129

1.1
Location : writeNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenReaderDecodesEachTableValueOnce()]
removed call to java/io/DataOutputStream::writeInt → KILLED

1130

1.1
Location : writeNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:writeToAndReadFromRejectNullArguments()]
changed conditional boundary → KILLED

2.2
Location : writeNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:writeToAndReadFromRejectNullArguments()]
negated conditional → KILLED

1131

1.1
Location : writeNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenReaderDecodesEachTableValueOnce()]
removed call to java/io/DataOutputStream::writeChar → KILLED

1133

1.1
Location : writeNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenWriterEncodesEachEqualityDistinctValueOnce()]
negated conditional → KILLED

1136

1.1
Location : writeNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenReaderDecodesEachTableValueOnce()]
removed call to java/io/DataOutputStream::writeInt → KILLED

1139

1.1
Location : writeNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenReaderDecodesEachTableValueOnce()]
removed call to java/io/DataOutputStream::writeInt → KILLED

1140

1.1
Location : writeNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:writeToAndReadFromRejectNullArguments()]
changed conditional boundary → KILLED

2.2
Location : writeNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:writeToAndReadFromRejectNullArguments()]
negated conditional → KILLED

1143

1.1
Location : writeNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenWriterEncodesEachEqualityDistinctValueOnce()]
negated conditional → KILLED

1144

1.1
Location : writeNode
Killed by : none
negated conditional → NO_COVERAGE

1149

1.1
Location : writeNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenReaderDecodesEachTableValueOnce()]
removed call to java/io/DataOutputStream::writeInt → KILLED

1150

1.1
Location : writeNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenReaderDecodesEachTableValueOnce()]
removed call to java/io/DataOutputStream::writeInt → KILLED

1156

1.1
Location : newSha256Digest
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:firstGetFingerprintCalculatesAndCachesFingerprint()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::newSha256Digest → KILLED

1169

1.1
Location : updateNodeFingerprint
Killed by : none
negated conditional → SURVIVED
Covering tests

2.2
Location : updateNodeFingerprint
Killed by : none
removed call to org/egothor/stemmer/FrequencyTrie::updateInt → SURVIVED Covering tests

1170

1.1
Location : updateNodeFingerprint
Killed by : none
removed call to org/egothor/stemmer/FrequencyTrie::updateInt → SURVIVED
Covering tests

1172

1.1
Location : updateNodeFingerprint
Killed by : none
removed call to org/egothor/stemmer/FrequencyTrie::updateInt → SURVIVED
Covering tests

1176

1.1
Location : updateNodeFingerprint
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:firstGetFingerprintCalculatesAndCachesFingerprint()]
negated conditional → KILLED

1179

1.1
Location : updateNodeFingerprint
Killed by : none
removed call to org/egothor/stemmer/FrequencyTrie::updateInt → SURVIVED
Covering tests

1182

1.1
Location : updateNodeFingerprint
Killed by : none
removed call to org/egothor/stemmer/FrequencyTrie::updateInt → SURVIVED
Covering tests

1184

1.1
Location : updateNodeFingerprint
Killed by : none
removed call to org/egothor/stemmer/FrequencyTrie::updateUtf8 → SURVIVED
Covering tests

1187

1.1
Location : updateNodeFingerprint
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:fingerprintReflectsMetadataAndCompiledTrieContent()]
removed call to org/egothor/stemmer/FrequencyTrie::updateInt → KILLED

1193

1.1
Location : updateUtf8
Killed by : none
removed call to org/egothor/stemmer/FrequencyTrie::updateInt → SURVIVED
Covering tests

1194

1.1
Location : updateUtf8
Killed by : none
removed call to java/security/MessageDigest::update → SURVIVED
Covering tests

1198

1.1
Location : updateInt
Killed by : none
removed call to java/security/MessageDigest::update → SURVIVED
Covering tests

2.2
Location : updateInt
Killed by : none
Replaced Unsigned Shift Right with Shift Left → SURVIVED Covering tests

1199

1.1
Location : updateInt
Killed by : none
removed call to java/security/MessageDigest::update → SURVIVED
Covering tests

2.2
Location : updateInt
Killed by : none
Replaced Unsigned Shift Right with Shift Left → SURVIVED Covering tests

1200

1.1
Location : updateInt
Killed by : none
removed call to java/security/MessageDigest::update → SURVIVED
Covering tests

2.2
Location : updateInt
Killed by : none
Replaced Unsigned Shift Right with Shift Left → SURVIVED Covering tests

1201

1.1
Location : updateInt
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:fingerprintReflectsMetadataAndCompiledTrieContent()]
removed call to java/security/MessageDigest::update → KILLED

1205

1.1
Location : toLowerHex
Killed by : none
Replaced integer multiplication with division → SURVIVED
Covering tests

1207

1.1
Location : toLowerHex
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:firstGetFingerprintCalculatesAndCachesFingerprint()]
Replaced Unsigned Shift Right with Shift Left → KILLED

2.2
Location : toLowerHex
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:firstGetFingerprintCalculatesAndCachesFingerprint()]
Replaced bitwise AND with OR → KILLED

3.3
Location : toLowerHex
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:firstGetFingerprintCalculatesAndCachesFingerprint()]
Replaced bitwise AND with OR → KILLED

1209

1.1
Location : toLowerHex
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:firstGetFingerprintCalculatesAndCachesFingerprint()]
replaced return value with "" for org/egothor/stemmer/FrequencyTrie::toLowerHex → KILLED

1228

1.1
Location : read
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsInvalidStreamMagicHeader()]
changed conditional boundary → KILLED

2.2
Location : read
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsInvalidStreamMagicHeader()]
negated conditional → KILLED

1234

1.1
Location : read
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsInvalidStreamMagicHeader()]
negated conditional → KILLED

1239

1.1
Location : read
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsNegativeNodeCount()]
negated conditional → KILLED

2.2
Location : read
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsInvalidTextualMetadataBlock()]
changed conditional boundary → KILLED

3.3
Location : read
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsNegativeNodeCount()]
changed conditional boundary → KILLED

4.4
Location : read
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsNegativeNodeCount()]
negated conditional → KILLED

1244

1.1
Location : read
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

2.2
Location : read
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsNegativeNodeCount()]
negated conditional → KILLED

1249

1.1
Location : read
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsInvalidTextualMetadataBlock()]
changed conditional boundary → KILLED

2.2
Location : read
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsInvalidRootNodeIdentifier()]
changed conditional boundary → KILLED

3.3
Location : read
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsInvalidRootNodeIdentifier()]
negated conditional → KILLED

4.4
Location : read
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsInvalidTextualMetadataBlock()]
negated conditional → KILLED

1254

1.1
Location : read
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsNonPositiveStoredCounts()]
negated conditional → KILLED

2.2
Location : read
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenReaderRejectsNegativeValueTableSize()]
changed conditional boundary → KILLED

1257

1.1
Location : read
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromSupportsLegacyVersionTwoMetadata()]
negated conditional → KILLED

2.2
Location : read
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRespectsDenseLookupMaxExpandedIndexOverride()]
changed conditional boundary → KILLED

1266

1.1
Location : read
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromSupportsLegacyVersionTwoMetadata()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::read → KILLED

1289

1.1
Location : readValueTable
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

2.2
Location : readValueTable
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenReaderRejectsNegativeValueTableSize()]
negated conditional → KILLED

1294

1.1
Location : readValueTable
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenReaderRejectsValueTableIndexEqualToSize()]
negated conditional → KILLED

2.2
Location : readValueTable
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenReaderRejectsTruncatedNodeLocalValueIndex()]
changed conditional boundary → KILLED

1297

1.1
Location : readValueTable
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenReaderRejectsValueTableIndexEqualToSize()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::readValueTable → KILLED

1301

1.1
Location : wrapInputStream
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsInvalidStreamMagicHeader()]
negated conditional → KILLED

2.2
Location : wrapInputStream
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsInvalidStreamMagicHeader()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::wrapInputStream → KILLED

1307

1.1
Location : readMetadata
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsInvalidTextualMetadataBlock()]
negated conditional → KILLED

2.2
Location : readMetadata
Killed by : org.egothor.stemmer.CompiledTrieArtifactRegressionTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.CompiledTrieArtifactRegressionTest]/[test-template:shouldLoadGoldenArtifactsDirectlyAsCompiledCommands(org.egothor.stemmer.CompiledTrieArtifactRegressionTest$ArtifactCase)]/[test-template-invocation:#2]
changed conditional boundary → KILLED

1308

1.1
Location : readMetadata
Killed by : org.egothor.stemmer.CompiledTrieArtifactRegressionTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.CompiledTrieArtifactRegressionTest]/[test-template:shouldLoadGoldenArtifactsDirectlyAsCompiledCommands(org.egothor.stemmer.CompiledTrieArtifactRegressionTest$ArtifactCase)]/[test-template-invocation:#2]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::readMetadata → KILLED

1312

1.1
Location : readMetadata
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsNonPositiveStoredCounts()]
negated conditional → KILLED

2.2
Location : readMetadata
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromParsesVersionThreeMetadata()]
changed conditional boundary → KILLED

1313

1.1
Location : readMetadata
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromSupportsLegacyVersionTwoMetadata()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::readMetadata → KILLED

1319

1.1
Location : readMetadata
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromParsesVersionThreeMetadata()]
negated conditional → KILLED

2.2
Location : readMetadata
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromParsesVersionFourCaseMetadata()]
changed conditional boundary → KILLED

1321

1.1
Location : readMetadata
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromParsesVersionThreeMetadata()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::readMetadata → KILLED

1328

1.1
Location : readTextMetadata
Killed by : org.egothor.stemmer.CompiledTrieArtifactRegressionTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.CompiledTrieArtifactRegressionTest]/[test-template:shouldLoadGoldenArtifactsDirectlyAsCompiledCommands(org.egothor.stemmer.CompiledTrieArtifactRegressionTest$ArtifactCase)]/[test-template-invocation:#2]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::readTextMetadata → KILLED

1336

1.1
Location : readTraversalDirection
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsInvalidLegacyMetadataOrdinal()]
negated conditional → KILLED

2.2
Location : readTraversalDirection
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsInvalidLegacyMetadataOrdinal()]
changed conditional boundary → KILLED

1337

1.1
Location : readTraversalDirection
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsNonPositiveStoredCounts()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::readTraversalDirection → KILLED

1339

1.1
Location : readTraversalDirection
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromSupportsLegacyVersionTwoMetadata()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::readTraversalDirection → KILLED

1346

1.1
Location : readReductionSettings
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromParsesVersionThreeMetadata()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::readReductionSettings → KILLED

1350

1.1
Location : readCaseProcessingMode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromParsesVersionFourCaseMetadata()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::readCaseProcessingMode → KILLED

1356

1.1
Location : readEnumByOrdinal
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromSupportsLegacyVersionTwoMetadata()]
negated conditional → KILLED

2.2
Location : readEnumByOrdinal
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

3.3
Location : readEnumByOrdinal
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromSupportsLegacyVersionTwoMetadata()]
changed conditional boundary → KILLED

4.4
Location : readEnumByOrdinal
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsInvalidLegacyMetadataOrdinal()]
negated conditional → KILLED

1359

1.1
Location : readEnumByOrdinal
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromSupportsLegacyVersionTwoMetadata()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::readEnumByOrdinal → KILLED

1373

1.1
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsCyclicSerializedNodeReferences()]
changed conditional boundary → KILLED

2.2
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsNonPositiveStoredCounts()]
negated conditional → KILLED

1374

1.1
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsNonPositiveStoredCounts()]
negated conditional → KILLED

2.2
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:metadataAwareReaderSupportsInlineValuesInVersionsOneThroughSix()]
changed conditional boundary → KILLED

1379

1.1
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsNonPositiveStoredCounts()]
negated conditional → KILLED

2.2
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsNonPositiveStoredCounts()]
changed conditional boundary → KILLED

1386

1.1
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsNonPositiveStoredCounts()]
negated conditional → KILLED

2.2
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsNonPositiveStoredCounts()]
changed conditional boundary → KILLED

1391

1.1
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsNonAscendingSerializedEdgeLabels()]
removed call to org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::validateSerializedEdges → KILLED

1394

1.1
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsCyclicSerializedNodeReferences()]
changed conditional boundary → KILLED

2.2
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsNonPositiveStoredCounts()]
negated conditional → KILLED

1402

1.1
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsCyclicSerializedNodeReferences()]
negated conditional → KILLED

2.2
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieLookupModeTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieLookupModeTest]/[method:acceptingWithChildrenSurvivesSerialization()]
negated conditional → KILLED

1409

1.1
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsCyclicSerializedNodeReferences()]
changed conditional boundary → KILLED

2.2
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsNonPositiveStoredCounts()]
negated conditional → KILLED

1410

1.1
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsNonPositiveStoredCounts()]
negated conditional → KILLED

2.2
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenReaderRejectsValueTableIndexEqualToSize()]
changed conditional boundary → KILLED

1412

1.1
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenReaderRejectsValueTableIndexEqualToSize()]
changed conditional boundary → KILLED

2.2
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenReaderRejectsTruncatedOccurrenceCount()]
changed conditional boundary → KILLED

3.3
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenReaderRejectsValueTableIndexEqualToSize()]
negated conditional → KILLED

4.4
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:versionSevenReaderRejectsTruncatedOccurrenceCount()]
negated conditional → KILLED

1422

1.1
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsNonPositiveStoredCounts()]
negated conditional → KILLED

2.2
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsNonPositiveStoredCounts()]
changed conditional boundary → KILLED

1433

1.1
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromSupportsLegacyVersionTwoMetadata()]
changed conditional boundary → KILLED

2.2
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsCyclicSerializedNodeReferences()]
negated conditional → KILLED

1438

1.1
Location : readNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromSupportsLegacyVersionTwoMetadata()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::readNodes → KILLED

1446

1.1
Location : resolveNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsCyclicSerializedNodeReferences()]
negated conditional → KILLED

1447

1.1
Location : resolveNode
Killed by : org.egothor.stemmer.CompiledTrieArtifactRegressionTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.CompiledTrieArtifactRegressionTest]/[test-template:shouldLoadGoldenArtifactsDirectlyAsCompiledCommands(org.egothor.stemmer.CompiledTrieArtifactRegressionTest$ArtifactCase)]/[test-template-invocation:#2]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::resolveNode → KILLED

1450

1.1
Location : resolveNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromSupportsLegacyVersionTwoMetadata()]
negated conditional → KILLED

1462

1.1
Location : resolveNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsCyclicSerializedNodeReferences()]
negated conditional → KILLED

2.2
Location : resolveNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromSupportsLegacyVersionTwoMetadata()]
changed conditional boundary → KILLED

1464

1.1
Location : resolveNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsCyclicSerializedNodeReferences()]
negated conditional → KILLED

2.2
Location : resolveNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsCyclicSerializedNodeReferences()]
changed conditional boundary → KILLED

3.3
Location : resolveNode
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

4.4
Location : resolveNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsCyclicSerializedNodeReferences()]
negated conditional → KILLED

1476

1.1
Location : resolveNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromSupportsLegacyVersionTwoMetadata()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$CompiledTrieReader::resolveNode → KILLED

1483

1.1
Location : validateSerializedEdges
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsCyclicSerializedNodeReferences()]
changed conditional boundary → KILLED

2.2
Location : validateSerializedEdges
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsNonPositiveStoredCounts()]
negated conditional → KILLED

1484

1.1
Location : validateSerializedEdges
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsNonAscendingSerializedEdgeLabels()]
negated conditional → KILLED

2.2
Location : validateSerializedEdges
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

3.3
Location : validateSerializedEdges
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsNonAscendingSerializedEdgeLabels()]
Replaced integer subtraction with addition → KILLED

1485

1.1
Location : validateSerializedEdges
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:readFromRejectsNonAscendingSerializedEdgeLabels()]
Replaced integer subtraction with addition → KILLED

1501

1.1
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:getEntriesReturnsSingleItemListForSingleStoredValue()]
negated conditional → KILLED

1502

1.1
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:getEntriesReturnsSingleItemListForSingleStoredValue()]
changed conditional boundary → KILLED

2.2
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:emptyTrieReturnsNullEmptyArrayAndEmptyEntries()]
Replaced integer subtraction with addition → KILLED

3.3
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:emptyKeyStoresValuesAtRootNode()]
negated conditional → KILLED

1503

1.1
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:getEntriesReturnsSingleItemListForSingleStoredValue()]
negated conditional → KILLED

1504

1.1
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieLookupModeTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieLookupModeTest]/[method:firstSelectsShallowestAccepting()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::findNode → KILLED

1507

1.1
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:emptyTrieReturnsNullEmptyArrayAndEmptyEntries()]
negated conditional → KILLED

1511

1.1
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:emptyKeyStoresValuesAtRootNode()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::findNode → KILLED

1514

1.1
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:shouldKeepExactLookupWhenUniformSubtreeContractionIsDisabled()]
negated conditional → KILLED

2.2
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:shouldKeepExactLookupWhenUniformSubtreeContractionIsDisabled()]
changed conditional boundary → KILLED

1515

1.1
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:shouldKeepExactLookupWhenUniformSubtreeContractionIsDisabled()]
negated conditional → KILLED

1516

1.1
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:shouldContractUniformInternalSubtreeIntoAcceptingLeaf()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::findNode → KILLED

1519

1.1
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:shouldKeepExactLookupWhenUniformSubtreeContractionIsDisabled()]
negated conditional → KILLED

1523

1.1
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:shouldKeepExactLookupWhenUniformSubtreeContractionIsDisabled()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::findNode → KILLED

1534

1.1
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupHonorsMaxResultsAndSinkEarlyStop()]
negated conditional → KILLED

1535

1.1
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupHonorsMaxResultsAndSinkEarlyStop()]
changed conditional boundary → KILLED

2.2
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupHonorsMaxResultsAndSinkEarlyStop()]
Replaced integer subtraction with addition → KILLED

3.3
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupHonorsMaxResultsAndSinkEarlyStop()]
negated conditional → KILLED

1536

1.1
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupHonorsMaxResultsAndSinkEarlyStop()]
negated conditional → KILLED

1537

1.1
Location : findNode
Killed by : none
replaced return value with null for org/egothor/stemmer/FrequencyTrie::findNode → NO_COVERAGE

1540

1.1
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupHonorsMaxResultsAndSinkEarlyStop()]
negated conditional → KILLED

1544

1.1
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupHonorsMaxResultsAndSinkEarlyStop()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::findNode → KILLED

1547

1.1
Location : findNode
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : findNode
Killed by : none
changed conditional boundary → NO_COVERAGE

1548

1.1
Location : findNode
Killed by : none
negated conditional → NO_COVERAGE

1549

1.1
Location : findNode
Killed by : none
replaced return value with null for org/egothor/stemmer/FrequencyTrie::findNode → NO_COVERAGE

1552

1.1
Location : findNode
Killed by : none
negated conditional → NO_COVERAGE

1556

1.1
Location : findNode
Killed by : none
replaced return value with null for org/egothor/stemmer/FrequencyTrie::findNode → NO_COVERAGE

1569

1.1
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupSupportsCharSlicesAndMetadataAwareKeys()]
negated conditional → KILLED

1570

1.1
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupSupportsCharSlicesAndMetadataAwareKeys()]
Replaced integer subtraction with addition → KILLED

2.2
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupSupportsCharSlicesAndMetadataAwareKeys()]
Replaced integer addition with subtraction → KILLED

3.3
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupSupportsCharSlicesAndMetadataAwareKeys()]
negated conditional → KILLED

4.4
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupSupportsCharSlicesAndMetadataAwareKeys()]
changed conditional boundary → KILLED

1571

1.1
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupSupportsCharSlicesAndMetadataAwareKeys()]
negated conditional → KILLED

1572

1.1
Location : findNode
Killed by : none
replaced return value with null for org/egothor/stemmer/FrequencyTrie::findNode → NO_COVERAGE

1575

1.1
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupSupportsCharSlicesAndMetadataAwareKeys()]
negated conditional → KILLED

1579

1.1
Location : findNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupSupportsCharSlicesAndMetadataAwareKeys()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::findNode → KILLED

1582

1.1
Location : findNode
Killed by : none
Replaced integer addition with subtraction → NO_COVERAGE

1583

1.1
Location : findNode
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : findNode
Killed by : none
changed conditional boundary → NO_COVERAGE

1584

1.1
Location : findNode
Killed by : none
negated conditional → NO_COVERAGE

1585

1.1
Location : findNode
Killed by : none
replaced return value with null for org/egothor/stemmer/FrequencyTrie::findNode → NO_COVERAGE

1588

1.1
Location : findNode
Killed by : none
negated conditional → NO_COVERAGE

1592

1.1
Location : findNode
Killed by : none
replaced return value with null for org/egothor/stemmer/FrequencyTrie::findNode → NO_COVERAGE

1604

1.1
Location : visitNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupHonorsMaxResultsAndSinkEarlyStop()]
negated conditional → KILLED

1610

1.1
Location : visitNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupHonorsMaxResultsAndSinkEarlyStop()]
negated conditional → KILLED

1616

1.1
Location : visitNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupHonorsMaxResultsAndSinkEarlyStop()]
changed conditional boundary → KILLED

2.2
Location : visitNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupHonorsMaxResultsAndSinkEarlyStop()]
negated conditional → KILLED

1617

1.1
Location : visitNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupHonorsMaxResultsAndSinkEarlyStop()]
Changed increment from 1 to -1 → KILLED

1618

1.1
Location : visitNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupHonorsMaxResultsAndSinkEarlyStop()]
negated conditional → KILLED

1622

1.1
Location : visitNode
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupHonorsMaxResultsAndSinkEarlyStop()]
replaced int return with 0 for org/egothor/stemmer/FrequencyTrie::visitNode → KILLED

1631

1.1
Location : validateMaxResults
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupRejectsNullAndInvalidRangeArguments()]
negated conditional → KILLED

2.2
Location : validateMaxResults
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:visitorLookupHandlesBoundaryCases()]
changed conditional boundary → KILLED

1643

1.1
Location : normalizeLookupKey
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:getEntriesReturnsSingleItemListForSingleStoredValue()]
replaced return value with "" for org/egothor/stemmer/FrequencyTrie::normalizeLookupKey → KILLED

1653

1.1
Location : normalizeLookupKey
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:lookupAppliesLowercaseNormalizationWhenMetadataRequiresIt()]
negated conditional → KILLED

2.2
Location : normalizeLookupKey
Killed by : none
negated conditional → SURVIVED
Covering tests

1654

1.1
Location : normalizeLookupKey
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:lookupKeepsCaseSensitiveBehaviorWhenMetadataIsAsIs()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::normalizeLookupKey → KILLED

1658

1.1
Location : normalizeLookupKey
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:diacriticRemoveModeStripsDictionaryAndLookupKeys()]
negated conditional → KILLED

1661

1.1
Location : normalizeLookupKey
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:diacriticRemoveModeStripsDictionaryAndLookupKeys()]
negated conditional → KILLED

1663

1.1
Location : normalizeLookupKey
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:emptyTrieReturnsNullEmptyArrayAndEmptyEntries()]
negated conditional → KILLED

1668

1.1
Location : normalizeLookupKey
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:emptyTrieReturnsNullEmptyArrayAndEmptyEntries()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie::normalizeLookupKey → KILLED

1869

1.1
Location : <init>
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:fallbackDiacriticModeIsNotSupportedYet()]
negated conditional → KILLED

2.2
Location : <init>
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

1923

1.1
Location : put
Killed by : org.egothor.stemmer.FrequencyTrieBuilderUpdateTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuilderUpdateTest]/[method:frequencyUpdatesRejectOverflow()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::put → KILLED

1950

1.1
Location : build
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:emptyTrieReturnsNullEmptyArrayAndEmptyEntries()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::build → KILLED

1982

1.1
Location : put
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:fallbackDiacriticModeIsNotSupportedYet()]
changed conditional boundary → KILLED

2.2
Location : put
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:fallbackDiacriticModeIsNotSupportedYet()]
negated conditional → KILLED

1988

1.1
Location : put
Killed by : org.egothor.stemmer.FrequencyTrieBuilderUpdateTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuilderUpdateTest]/[method:frequencyUpdatesRejectOverflow()]
negated conditional → KILLED

1989

1.1
Location : put
Killed by : org.egothor.stemmer.FrequencyTrieBuildersTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuildersTest]/[method:shouldPreserveAggregatedCountsOfSharedCompiledNodes()]
removed call to org/egothor/stemmer/FrequencyTrie$Builder::markModified → KILLED

1991

1.1
Location : put
Killed by : org.egothor.stemmer.FrequencyTrieBuilderUpdateTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuilderUpdateTest]/[method:frequencyUpdatesRejectOverflow()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::put → KILLED

2025

1.1
Location : putDominant
Killed by : org.egothor.stemmer.FrequencyTrieBuilderUpdateTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuilderUpdateTest]/[method:frequencyUpdatesRejectOverflow()]
negated conditional → KILLED

2029

1.1
Location : putDominant
Killed by : org.egothor.stemmer.FrequencyTrieBuilderUpdateTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuilderUpdateTest]/[method:updatesAreChainable()]
negated conditional → KILLED

2033

1.1
Location : putDominant
Killed by : none
removed call to org/egothor/stemmer/FrequencyTrie$Builder::markModified → SURVIVED
Covering tests

2034

1.1
Location : putDominant
Killed by : org.egothor.stemmer.FrequencyTrieBuilderUpdateTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuilderUpdateTest]/[method:putDominantOverridesHigherFrequency()]
Replaced integer addition with subtraction → KILLED

2035

1.1
Location : putDominant
Killed by : org.egothor.stemmer.FrequencyTrieBuilderUpdateTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuilderUpdateTest]/[method:updatesAreChainable()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::putDominant → KILLED

2058

1.1
Location : set
Killed by : none
removed call to org/egothor/stemmer/FrequencyTrie$Builder::markModified → SURVIVED
Covering tests

2060

1.1
Location : set
Killed by : org.egothor.stemmer.FrequencyTrieBuilderUpdateTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuilderUpdateTest]/[method:setReplacesAllValues()]
removed call to java/util/Map::clear → KILLED

2062

1.1
Location : set
Killed by : org.egothor.stemmer.FrequencyTrieBuilderUpdateTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuilderUpdateTest]/[method:updatesAreChainable()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::set → KILLED

2081

1.1
Location : putIfAbsent
Killed by : org.egothor.stemmer.FrequencyTrieBuilderUpdateTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuilderUpdateTest]/[method:putIfAbsentOnlyWhenEmpty()]
negated conditional → KILLED

2082

1.1
Location : putIfAbsent
Killed by : none
removed call to org/egothor/stemmer/FrequencyTrie$Builder::markModified → SURVIVED
Covering tests

2085

1.1
Location : putIfAbsent
Killed by : org.egothor.stemmer.FrequencyTrieBuilderUpdateTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuilderUpdateTest]/[method:updatesAreChainable()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::putIfAbsent → KILLED

2112

1.1
Location : remove
Killed by : org.egothor.stemmer.FrequencyTrieBuilderUpdateTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuilderUpdateTest]/[method:removeKeyClearsExactNode()]
negated conditional → KILLED

2.2
Location : remove
Killed by : org.egothor.stemmer.FrequencyTrieBuilderUpdateTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuilderUpdateTest]/[method:removeKeyClearsExactNode()]
negated conditional → KILLED

2113

1.1
Location : remove
Killed by : none
removed call to org/egothor/stemmer/FrequencyTrie$Builder::markModified → SURVIVED
Covering tests

2114

1.1
Location : remove
Killed by : org.egothor.stemmer.FrequencyTrieBuilderUpdateTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuilderUpdateTest]/[method:removeKeyClearsExactNode()]
removed call to java/util/Map::clear → KILLED

2115

1.1
Location : remove
Killed by : org.egothor.stemmer.FrequencyTrieBuildersTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuildersTest]/[method:shouldRemoveContractedAcceptingGeneralization()]
removed call to org/egothor/stemmer/trie/MutableNode::clearAcceptsRemainingInput → KILLED

2117

1.1
Location : remove
Killed by : org.egothor.stemmer.FrequencyTrieBuilderUpdateTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuilderUpdateTest]/[method:updatesAreChainable()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::remove → KILLED

2134

1.1
Location : remove
Killed by : org.egothor.stemmer.FrequencyTrieBuilderUpdateTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuilderUpdateTest]/[method:removeKeyValueDropsOne()]
negated conditional → KILLED

2.2
Location : remove
Killed by : org.egothor.stemmer.FrequencyTrieBuilderUpdateTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuilderUpdateTest]/[method:removeKeyValueDropsOne()]
negated conditional → KILLED

2135

1.1
Location : remove
Killed by : none
removed call to org/egothor/stemmer/FrequencyTrie$Builder::markModified → SURVIVED
Covering tests

2137

1.1
Location : remove
Killed by : none
negated conditional → SURVIVED
Covering tests

2138

1.1
Location : remove
Killed by : none
removed call to org/egothor/stemmer/trie/MutableNode::clearAcceptsRemainingInput → NO_COVERAGE

2141

1.1
Location : remove
Killed by : org.egothor.stemmer.FrequencyTrieBuilderUpdateTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuilderUpdateTest]/[method:updatesAreChainable()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::remove → KILLED

2153

1.1
Location : findMutableNode
Killed by : org.egothor.stemmer.FrequencyTrieBuilderUpdateTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuilderUpdateTest]/[method:updatesAreChainable()]
changed conditional boundary → KILLED

2.2
Location : findMutableNode
Killed by : org.egothor.stemmer.FrequencyTrieBuilderUpdateTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuilderUpdateTest]/[method:removeKeyClearsExactNode()]
negated conditional → KILLED

2157

1.1
Location : findMutableNode
Killed by : org.egothor.stemmer.FrequencyTrieBuilderUpdateTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuilderUpdateTest]/[method:removeKeyClearsExactNode()]
negated conditional → KILLED

2161

1.1
Location : findMutableNode
Killed by : org.egothor.stemmer.FrequencyTrieBuilderUpdateTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuilderUpdateTest]/[method:removeKeyClearsExactNode()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::findMutableNode → KILLED

2173

1.1
Location : navigateToNode
Killed by : org.egothor.stemmer.FrequencyTrieBuilderUpdateTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuilderUpdateTest]/[method:updatesAreChainable()]
changed conditional boundary → KILLED

2.2
Location : navigateToNode
Killed by : org.egothor.stemmer.FrequencyTrieBuildersTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuildersTest]/[method:shouldReconstructEmptyTrie()]
negated conditional → KILLED

2177

1.1
Location : navigateToNode
Killed by : org.egothor.stemmer.FrequencyTrieBuilderUpdateTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuilderUpdateTest]/[method:updatesAreChainable()]
negated conditional → KILLED

2183

1.1
Location : navigateToNode
Killed by : org.egothor.stemmer.FrequencyTrieBuilderUpdateTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuilderUpdateTest]/[method:updatesAreChainable()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::navigateToNode → KILLED

2199

1.1
Location : markAcceptsRemainingInput
Killed by : org.egothor.stemmer.FrequencyTrieBuildersTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuildersTest]/[method:shouldRemoveContractedAcceptingGeneralization()]
removed call to org/egothor/stemmer/trie/MutableNode::markAcceptsRemainingInput → KILLED

2200

1.1
Location : markAcceptsRemainingInput
Killed by : none
replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::markAcceptsRemainingInput → SURVIVED
Covering tests

2226

1.1
Location : recordCompiledSource
Killed by : org.egothor.stemmer.FrequencyTrieBuildersTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuildersTest]/[method:shouldReconstructEmptyTrie()]
negated conditional → KILLED

2.2
Location : recordCompiledSource
Killed by : none
negated conditional → NO_COVERAGE

2230

1.1
Location : recordCompiledSource
Killed by : none
replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::recordCompiledSource → SURVIVED
Covering tests

2248

1.1
Location : markModified
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:dominantReductionMergesQualifiedDominantWinnerNodes()]
negated conditional → KILLED

2249

1.1
Location : lambda$markModified$0
Killed by : org.egothor.stemmer.FrequencyTrieBuildersTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuildersTest]/[method:shouldPreserveAggregatedCountsOfSharedCompiledNodes()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::lambda$markModified$0 → KILLED

2263

1.1
Location : normalizeDictionaryKey
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:lookupKeepsCaseSensitiveBehaviorWhenMetadataIsAsIs()]
negated conditional → KILLED

2267

1.1
Location : normalizeDictionaryKey
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:fallbackDiacriticModeIsNotSupportedYet()]
negated conditional → KILLED

2269

1.1
Location : normalizeDictionaryKey
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:fallbackDiacriticModeIsNotSupportedYet()]
negated conditional → KILLED

2274

1.1
Location : normalizeDictionaryKey
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:getEntriesReturnsSingleItemListForSingleStoredValue()]
replaced return value with "" for org/egothor/stemmer/FrequencyTrie$Builder::normalizeDictionaryKey → KILLED

2288

1.1
Location : buildTimeSize
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:reductionMateriallyDecreasesCompiledTrieSizeForRepeatedEquivalentSuffixes()]
replaced int return with 0 for org/egothor/stemmer/FrequencyTrie$Builder::buildTimeSize → KILLED

2297

1.1
Location : traversalDirection
Killed by : none
replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::traversalDirection → NO_COVERAGE

2309

1.1
Location : countMutableNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:reductionMateriallyDecreasesCompiledTrieSizeForRepeatedEquivalentSuffixes()]
Replaced integer addition with subtraction → KILLED

2311

1.1
Location : countMutableNodes
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:reductionMateriallyDecreasesCompiledTrieSizeForRepeatedEquivalentSuffixes()]
replaced int return with 0 for org/egothor/stemmer/FrequencyTrie$Builder::countMutableNodes → KILLED

2331

1.1
Location : reduce
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:trieRejectsNullLookupKeys()]
negated conditional → KILLED

2337

1.1
Location : reduce
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:getEntriesReturnsSingleItemListForSingleStoredValue()]
negated conditional → KILLED

2339

1.1
Location : reduce
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:shouldContractUniformInternalSubtreeIntoAcceptingLeaf()]
negated conditional → KILLED

2352

1.1
Location : reduce
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:trieRejectsNullLookupKeys()]
negated conditional → KILLED

2354

1.1
Location : reduce
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:dominantReductionMergesQualifiedDominantWinnerNodes()]
removed call to org/egothor/stemmer/trie/ReductionContext::register → KILLED

2355

1.1
Location : reduce
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:trieRejectsNullLookupKeys()]
negated conditional → KILLED

2358

1.1
Location : reduce
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:trieRejectsNullLookupKeys()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::reduce → KILLED

2361

1.1
Location : reduce
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:shouldKeepExactLookupWhenUniformSubtreeContractionIsDisabled()]
negated conditional → KILLED

2362

1.1
Location : reduce
Killed by : org.egothor.stemmer.FrequencyTrieBuildersTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuildersTest]/[method:shouldPreserveAggregatedCountsOfSharedCompiledNodes()]
negated conditional → KILLED

2363

1.1
Location : reduce
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:dominantReductionMergesQualifiedDominantWinnerNodes()]
removed call to org/egothor/stemmer/trie/ReducedNode::mergeLocalCounts → KILLED

2365

1.1
Location : reduce
Killed by : none
removed call to org/egothor/stemmer/trie/ReducedNode::mergeChildren → SURVIVED
Covering tests

2367

1.1
Location : reduce
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:shouldKeepExactLookupWhenUniformSubtreeContractionIsDisabled()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::reduce → KILLED

2381

1.1
Location : contractUniformSubtree
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:shouldContractUniformInternalSubtreeIntoAcceptingLeaf()]
negated conditional → KILLED

2388

1.1
Location : contractUniformSubtree
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:shouldContractUniformInternalSubtreeIntoAcceptingLeaf()]
negated conditional → KILLED

2389

1.1
Location : contractUniformSubtree
Killed by : none
negated conditional → SURVIVED
Covering tests

2398

1.1
Location : contractUniformSubtree
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:shouldContractUniformInternalSubtreeIntoAcceptingLeaf()]
negated conditional → KILLED

2402

1.1
Location : contractUniformSubtree
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:shouldContractUniformInternalSubtreeIntoAcceptingLeaf()]
negated conditional → KILLED

2.2
Location : contractUniformSubtree
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:shouldContractUniformInternalSubtreeIntoAcceptingLeaf()]
negated conditional → KILLED

2409

1.1
Location : contractUniformSubtree
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:shouldContractUniformInternalSubtreeIntoAcceptingLeaf()]
negated conditional → KILLED

2415

1.1
Location : contractUniformSubtree
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:shouldContractUniformInternalSubtreeIntoAcceptingLeaf()]
replaced return value with Collections.emptyMap for org/egothor/stemmer/FrequencyTrie$Builder::contractUniformSubtree → KILLED

2425

1.1
Location : isSingleValueLeaf
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:shouldContractUniformInternalSubtreeIntoAcceptingLeaf()]
negated conditional → KILLED

2.2
Location : isSingleValueLeaf
Killed by : org.egothor.stemmer.FrequencyTrieBuildersTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieBuildersTest]/[method:shouldRemoveContractedAcceptingGeneralization()]
replaced boolean return with true for org/egothor/stemmer/FrequencyTrie$Builder::isSingleValueLeaf → KILLED

3.3
Location : isSingleValueLeaf
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:shouldContractUniformInternalSubtreeIntoAcceptingLeaf()]
negated conditional → KILLED

2438

1.1
Location : freeze
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:trieRejectsNullLookupKeys()]
negated conditional → KILLED

2439

1.1
Location : freeze
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:shouldKeepExactLookupWhenUniformSubtreeContractionIsDisabled()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::freeze → KILLED

2447

1.1
Location : freeze
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:diacriticRemoveModeStripsDictionaryAndLookupKeys()]
removed call to java/util/List::sort → KILLED

2453

1.1
Location : freeze
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:trieRejectsNullLookupKeys()]
changed conditional boundary → KILLED

2.2
Location : freeze
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:trieRejectsNullLookupKeys()]
negated conditional → KILLED

2462

1.1
Location : freeze
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:trieRejectsNullLookupKeys()]
replaced return value with null for org/egothor/stemmer/FrequencyTrie$Builder::freeze → KILLED

2473

1.1
Location : copyCounts
Killed by : org.egothor.stemmer.FrequencyTrieTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.FrequencyTrieTest]/[method:emptyKeyStoresValuesAtRootNode()]
replaced return value with Collections.emptyMap for org/egothor/stemmer/FrequencyTrie$Builder::copyCounts → KILLED

Active mutators

Tests examined


Report generated by PIT 1.22.1