예제 #1
0
        /// <summary>
        /// Creates a clone of the current node as a sequence terminator node.
        /// </summary>
        /// <returns>The current node cloned as a sequence terminator.</returns>
        /// <remarks>This method is primarily included for the case where a sequence is added to the trie which is a sub-sequence of an existing sequence (e.g. in the case of adding 'judge' when 'judgement' already exists).  In such a case the last item in the new sequence being added needs to be changed from a TrieNode to a SequenceTerminatorTrieNode.</remarks>
        public SequenceTerminatorTrieNode <T> CloneAsSequenceTerminator()
        {
            SequenceTerminatorTrieNode <T> clonedNode = new SequenceTerminatorTrieNode <T>(this.Item, this.ParentNode);

            foreach (KeyValuePair <T, TrieNode <T> > currentChildNode in childNodes)
            {
                clonedNode.AddChildNode(currentChildNode.Key, currentChildNode.Value);
                clonedNode.SetSubTreeSize(currentChildNode.Key, this.subtreeSizes[currentChildNode.Key]);
            }

            return(clonedNode);
        }
        /// <summary>
        /// Creates a replica of the current node (i.e. a copy of the current node where the parent and child nodes hold the same item, but are not the same parent and child nodes as those of the current node).
        /// </summary>
        /// <returns>A replicated copy of the current node.</returns>
        /// <remarks>This method is primarily included for unit tests where visibility of the internal trie structure is required, but without the risk of clients being able to modify the internal structure.</remarks>
        public new SequenceTerminatorTrieNode <T> Replicate()
        {
            SequenceTerminatorTrieNode <T> replicaNode;

            if (this.ParentNode != null)
            {
                replicaNode = new SequenceTerminatorTrieNode <T>(this.Item, new SequenceTerminatorTrieNode <T>(this.ParentNode.Item, null));
            }
            else
            {
                replicaNode = new SequenceTerminatorTrieNode <T>(this.Item, null);
            }
            foreach (T currentChildNodeItem in childNodes.Keys)
            {
                replicaNode.AddChildNode(currentChildNodeItem, new TrieNode <T>(currentChildNodeItem, replicaNode));
                replicaNode.SetSubTreeSize(currentChildNodeItem, this.subtreeSizes[currentChildNodeItem]);
            }

            return(replicaNode);
        }
        /// <summary>
        /// Adds the specified sequence of items to the trie.
        /// </summary>
        /// <param name="sequence">The sequence of items to add.</param>
        /// <exception cref="System.ArgumentException">The specified sequence is empty.</exception>
        /// <exception cref="System.ArgumentException">The specified sequence already exists in the trie.</exception>
        public void Insert(IList <T> sequence)
        {
            ThrowExceptionIfSequenceCountIsZero(sequence);

            TrieNode <T> currentNode = rootNode;

            for (Int32 i = 0; i < sequence.Count; i++)
            {
                // Need special handling for the last item in the sequence
                if (i == (sequence.Count - 1))
                {
                    if (currentNode.ChildNodeExists(sequence[i]) == false)
                    {
                        // Create a new terminator node to hold the last item in the sequence
                        SequenceTerminatorTrieNode <T> newNode = new SequenceTerminatorTrieNode <T>(sequence[i], currentNode);
                        currentNode.AddChildNode(sequence[i], newNode);
                    }
                    else
                    {
                        TrieNode <T> existingNode = currentNode.GetChildNode(sequence[i]);
                        if (existingNode is SequenceTerminatorTrieNode <T> )
                        {
                            // If a node for the last item already exists and is a terminator node, throw an exception
                            Action <TrieNode <T>, TrieNode <T> > decrementCountsAction = (parentNode, childNode) =>
                            {
                                if (childNode != null)
                                {
                                    parentNode.DecrementSubtreeSize(childNode.Item);
                                }
                            };
                            TraverseUpFromNode(existingNode.ParentNode, decrementCountsAction);

                            throw new ArgumentException($"The specified sequence {{ {ConvertSequenceToString(sequence)} }} already exists in the trie.", nameof(sequence));
                        }
                        else
                        {
                            // Convert the existing node for the last item into a terminator node
                            Int32 previousSubtreeSize = currentNode.GetSubtreeSize(sequence[i]);
                            SequenceTerminatorTrieNode <T> terminatorNode = existingNode.CloneAsSequenceTerminator();
                            currentNode.RemoveChildNode(sequence[i]);
                            currentNode.AddChildNode(sequence[i], terminatorNode);
                            currentNode.SetSubTreeSize(sequence[i], previousSubtreeSize + 1);
                        }
                    }
                }
                else
                {
                    if (currentNode.ChildNodeExists(sequence[i]) == false)
                    {
                        TrieNode <T> newNode = new TrieNode <T>(sequence[i], currentNode);
                        currentNode.AddChildNode(sequence[i], newNode);
                    }
                    else
                    {
                        currentNode.IncrementSubtreeSize(sequence[i]);
                    }
                }

                currentNode = currentNode.GetChildNode(sequence[i]);
            }
        }