/// <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]); } }