< Summary

Information
Class: MoreStructures.SuffixTries.Builders.NaivePartiallyRecursiveSuffixTrieBuilder
Assembly: MoreStructures
File(s): /home/runner/work/MoreStructures/MoreStructures/MoreStructures/SuffixTries/Builders/NaivePartiallyRecursiveSuffixTrieBuilder.cs
Line coverage
100%
Covered lines: 34
Uncovered lines: 0
Coverable lines: 34
Total lines: 89
Line coverage: 100%
Branch coverage
100%
Covered branches: 8
Total branches: 8
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
BuildTree(...)100%2100%
IncludeSuffixIntoTrie(...)100%6100%

File(s)

/home/runner/work/MoreStructures/MoreStructures/MoreStructures/SuffixTries/Builders/NaivePartiallyRecursiveSuffixTrieBuilder.cs

#LineLine coverage
 1using MoreStructures.SuffixStructures.Builders;
 2
 3namespace MoreStructures.SuffixTries.Builders;
 4
 5/// <summary>
 6/// Builds objects, such as edges and nodes, for <see cref="SuffixTrieNode"/> structures.
 7/// </summary>
 8/// <remarks>
 9///     <para id="algo">
 10///     ALGORITHM
 11///     <br/>
 12///     Implemented iteratively, with one level of recursion per char of each suffix of the input
 13///     <see cref="TextWithTerminator"/> (where the longest suffix is the text itself).
 14///     </para>
 15///     <para id="advantages">
 16///     ADVANTAGES AND DISADVANTAGES
 17///     <br/>
 18///     Limited by call stack depth and usable with input text of a "reasonable" length (i.e. string having a length
 19///     &lt; ~1K chars).
 20///     </para>
 21///     <para id="complexity">
 22///     COMPLEXITY
 23///     <br/>
 24///     - Time Complexity = O(t^2 * as) and Space Complexity = O(t^2) where t = length of the text to match and
 25///       as = size of the alphabet of the text.
 26///       <br/>
 27///     - If the alphabet is of constant size, complexity is quadratic.
 28///     </para>
 29/// </remarks>
 30public class NaivePartiallyRecursiveSuffixTrieBuilder
 31    : IBuilder<SuffixTrieEdge, SuffixTrieNode>
 32{
 33    /// <inheritdoc path="//*[not(self::summary or self::remarks)]"/>
 34    /// <summary>
 35    ///     <inheritdoc/>
 36    /// </summary>
 37    /// <remarks>
 38    ///     <inheritdoc cref="NaivePartiallyRecursiveSuffixTrieBuilder"/>
 39    /// </remarks>
 40    public SuffixTrieNode BuildTree(params TextWithTerminator[] texts)
 5041    {
 5042        var (fullText, terminators) = texts.GenerateFullText();
 43
 5044        SuffixTrieNode root = new SuffixTrieNode.Leaf(0);
 45
 73046        for (var suffixIndex = 0; suffixIndex < fullText.Length; suffixIndex++)
 31547            root = IncludeSuffixIntoTrie(fullText, terminators, suffixIndex, suffixIndex, root);
 48
 5049        return root;
 5050    }
 51
 52    private static SuffixTrieNode.Intermediate IncludeSuffixIntoTrie(
 53        TextWithTerminator fullText, ISet<char> separators, int suffixCurrentIndex, int suffixIndex,
 54        SuffixTrieNode node)
 55655    {
 55656        var nodeChildren = new Dictionary<SuffixTrieEdge, SuffixTrieNode>(node.Children);
 55657        var currentChar = fullText[suffixCurrentIndex];
 152758        var childEdgeAndNode = nodeChildren.SingleOrDefault(c => fullText[c.Key.Index] == currentChar);
 59
 55660        if (childEdgeAndNode is (SuffixTrieEdge edge, SuffixTrieNode child))
 24161        {
 62            // Recurse on the child, removing the first char from the current suffix
 24163            nodeChildren[edge] = IncludeSuffixIntoTrie(
 24164                fullText,
 24165                separators,
 24166                suffixCurrentIndex: suffixCurrentIndex + 1,
 24167                suffixIndex: suffixIndex,
 24168                node: child);
 24169        }
 70        else
 31571        {
 72            // Build the entire path down to the first non-terminator char
 31573            child = new SuffixTrieNode.Leaf(suffixIndex);
 74
 31575            var indexOfFirstTerminator = Enumerable
 31576                .Range(suffixCurrentIndex, fullText.Length - suffixCurrentIndex)
 111077                .First(i => separators.Contains(fullText[i]));
 78
 159079            for (int i = indexOfFirstTerminator; i >= suffixCurrentIndex + 1; i--)
 48080            {
 48081                child = new SuffixTrieNode.Intermediate(
 48082                    new Dictionary<SuffixTrieEdge, SuffixTrieNode> { [new(i)] = child });
 48083            }
 31584            nodeChildren[new(suffixCurrentIndex)] = child;
 31585        }
 86
 55687        return new SuffixTrieNode.Intermediate(nodeChildren);
 55688    }
 89}