/// <summary> /// Raises the <see cref="TstTraverser.HighChild"/> event. /// </summary> /// <param name="p">high child about to be added to the tree</param> protected virtual void OnLowChild(TstDictionaryEntry p) { if (LowChild != null) LowChild(this, new TstDictionaryEntryEventArgs(p) ); }
internal void GetKeysRecursion(TstDictionaryEntry currentNode, ref StringCollection keyList, ref int keysNumReturnValues) { if (currentNode == null) { return; } if (currentNode.LowChild != null && (keysNumReturnValues == -1 || keysNumReturnValues > 0)) { GetKeysRecursion(currentNode.LowChild, ref keyList, ref keysNumReturnValues); } if (currentNode.IsKey && (keysNumReturnValues == -1 || keysNumReturnValues > 0)) { keyList.Add(currentNode.Key); DecreaseRecursionCount(ref keysNumReturnValues); } if (currentNode.EqChild != null && (keysNumReturnValues == -1 || keysNumReturnValues > 0)) { GetKeysRecursion(currentNode.EqChild, ref keyList, ref keysNumReturnValues); } if (currentNode.HighChild != null && (keysNumReturnValues == -1 || keysNumReturnValues > 0)) { GetKeysRecursion(currentNode.HighChild, ref keyList, ref keysNumReturnValues); } }
/// <summary> /// Create a dictionary with a specified root. /// </summary> /// <param name="root">Root of the new dictionary</param> protected TstDictionary(TstDictionaryEntry root) { if (root == null) throw new ArgumentNullException("root is null"); this.root = root; this.version = 0; }
/// <summary> /// Raises the <see cref="TstTraverser.TreeEntry"/> event. /// </summary> /// <param name="p">tst node added to the tree</param> protected virtual void OnTreeEntry(TstDictionaryEntry p) { if (TreeEntry != null) TreeEntry(this, new TstDictionaryEntryEventArgs(p) ); }
/// <summary> /// Raises the <see cref="TstTraverser.HighChild"/> event. /// </summary> /// <param name="p">high child about to be added to the tree</param> protected virtual void OnLowChild(TstDictionaryEntry p) { if (LowChild != null) { LowChild(this, new TstDictionaryEntryEventArgs(p) ); } }
/// <summary> /// Create a dictionary with a specified root. /// </summary> /// <param name="root">Root of the new dictionary</param> protected TstDictionary(TstDictionaryEntry root) { if (root == null) { throw new ArgumentNullException("root is null"); } this.root = root; this.version = 0; }
/// <summary> /// /// </summary> /// <param name="p"></param> /// <param name="key"></param> /// <param name="index"></param> /// <param name="dist"></param> /// <param name="matches"></param> internal void NearNeighborsSearch( TstDictionaryEntry p, string key, int index, int dist, IList matches ) { if (p == null || dist < 0) { return; } char c = key[index]; // low child if (dist > 0 || c < p.SplitChar) { NearNeighborsSearch(p.LowChild, key, index, dist, matches); } // eq child if (p.IsKey) { if (key.Length - index <= dist) { matches.Add(new DictionaryEntry(p.Key, p.Value)); } } else { int localIndex = index; if (localIndex != key.Length - 1) { ++localIndex; } int localDist = dist; if (c != p.SplitChar) { --localDist; } NearNeighborsSearch( p.EqChild, key, localIndex, localDist, matches ); } // highchild if (dist > 0 || c > p.SplitChar) { NearNeighborsSearch(p.HighChild, key, index, dist, matches); } }
/// <summary>Constructs an enumerator over <paramref name="tst"/></summary> /// <param name="tst">dictionary to enumerate.</param> /// <exception cref="ArgumentNullException">tst is null</exception> public TstDictionaryEnumerator(TstDictionary tst) { if (tst == null) throw new ArgumentNullException("tst"); this.version = tst.Version; this.dictionary = tst; this.currentNode = null; this.stack = null; }
/// <summary> /// Raises the <see cref="TstTraverser.TreeEntry"/> event. /// </summary> /// <param name="p">tst node added to the tree</param> protected virtual void OnTreeEntry(TstDictionaryEntry p) { if (TreeEntry != null) { TreeEntry(this, new TstDictionaryEntryEventArgs(p) ); } }
/// <summary> /// Determines whether the <see cref="TstDictionary"/> contains a specific key. /// </summary> /// <param name="key">The key to locate in the <see cref="TstDictionary"/>.</param> /// <returns>true if the <see cref="TstDictionary"/> contains an element with the specified key; otherwise, false.</returns> /// <exception cref="ArgumentNullException"> /// <paramref name="key"/> is a null reference (Nothing in Visual Basic). /// </exception> /// <remarks> /// <para>Complexity: Uses a Ternary Search Tree (tst) to find the key.</para> /// <para>The method behaves exactly as <see cref="TstDictionary.Contains"/>.</para> /// </remarks> public virtual bool ContainsKey(String key) { if (key == null) { throw new ArgumentNullException("key"); } TstDictionaryEntry de = Find(key); return(de != null && de.IsKey); }
/// <summary> /// Removes all elements from the <see cref="TstDictionary"/>. /// </summary> /// <exception cref="NotSupportedException">The <see cref="TstDictionary"/> is read-only.</exception> public virtual void Clear() { if (IsReadOnly) { throw new NotSupportedException("dictionary is read-only"); } // updating version ++version; root = null; }
/// <summary>Constructs an enumerator over <paramref name="tst"/></summary> /// <param name="tst">dictionary to enumerate.</param> /// <exception cref="ArgumentNullException">tst is null</exception> public TstDictionaryEnumerator(TstDictionary tst) { if (tst == null) { throw new ArgumentNullException("tst"); } this.version = tst.Version; this.dictionary = tst; this.currentNode = null; this.stack = null; }
///<summary> /// Removes the element with the specified key from the <see cref="TstDictionary"/>. /// </summary> /// <param name="key">The key of the element to remove.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="key"/> is a null reference (Nothing in Visual Basic). /// </exception> /// <exception cref="ArgumentException"><paramref name="key"/> is an empty string</exception> /// <exception cref="NotSupportedException">The <see cref="TstDictionary"/> is read-only.</exception> /// <exception cref="NotSupportedException">The <see cref="TstDictionary"/> has a fixed size.</exception> public virtual void Remove(String key) { if (key == null) { throw new ArgumentNullException("key is null"); } if (key.Length == 0) { throw new ArgumentException("key length cannot be 0"); } if (IsReadOnly) { throw new NotSupportedException("dictionary is read-only"); } if (IsFixedSize) { throw new NotSupportedException("dictionary has fixed size"); } // updating version ++version; TstDictionaryEntry p = Find(key); if (p == null) { return; } p.IsKey = false; p.Key = null; while (!p.IsKey && !p.HasChildren && p.Parent != null) { if (p.IsLowChild) { p.Parent.LowChild = null; } else if (p.IsHighChild) { p.Parent.HighChild = null; } else { p.Parent.EqChild = null; } p = p.Parent; } if (!p.IsKey && !p.HasChildren && p == root) { root = null; } }
/// <summary> /// Finds the tst node matching the key. /// </summary> /// <returns>the <see cref="TstDictionaryEntry"/> mathcing the key, null if not found.</returns> /// <exception cref="ArgumentNullException"><paramref name="key"/> is null.</exception> public virtual TstDictionaryEntry FindNoCase(String key) { if (key == null) { throw new ArgumentNullException("key"); } if (key.Length == 0) { return(null); } TstDictionaryEntry entry = Root; int index = 0; char c; // While index is less than our key.Length && Root is != null while (index < key.Length && entry != null) { c = Char.ToUpper(key[index]); if (c < Char.ToUpper(entry.SplitChar)) { entry = entry.LowChild; } else if (c > Char.ToUpper(entry.SplitChar)) { entry = entry.HighChild; } else { if (index == key.Length - 1) { break; } else { ++index; entry = entry.EqChild; } } if (entry != null && entry.IsKey && String.Compare(entry.Key, key, true) == 0) { //This is it!!!! return(entry); } } //If we get here we never found it return(null); }
/// <summary> /// Construct a tst node. /// </summary> /// <param name="parent">parent node</param> /// <param name="splitChar">split character</param> public TstDictionaryEntry( TstDictionaryEntry parent, char splitChar ) { this.isKey = false; this.key = null; this.value = null; this.parent = parent; this.splitChar = splitChar; this.lowChild = null; this.eqChild = null; this.highChild = null; }
/// <summary> /// Gets or sets the value associated with the specified key. /// </summary> /// <remarks> /// [C#] In C#, this property is the indexer for the <see cref="TstDictionary"/> class. /// </remarks> /// <param name="key">The key whose value to get or set.</param> /// <value> /// The value associated with the specified key. /// If the specified key is not found, attempting to get it returns a null reference /// (Nothing in Visual Basic), and attempting to set it creates a new element using the specified key. /// </value> /// <exception cref="ArgumentNullException"><paramref name="key"/> is a null reference</exception> /// <exception cref="ArgumentException"> /// The property is set and <paramref name="key"/> is an empty string /// </exception> /// <exception cref="NotSupportedException"> /// The property is set and the <see cref="TstDictionary"/> is read-only. /// </exception> /// <exception cref="NotSupportedException"> /// The property is set, <paramref name="key"/> does not exist in the collection, /// and the <see cref="TstDictionary"/> has a fixed size. /// </exception> public virtual Object this[String key] { get { if (key == null) { throw new ArgumentNullException("key"); } TstDictionaryEntry de = Find(key); if (de == null) { return(null); } else { return(de.Value); } } set { if (key == null) { throw new ArgumentNullException("key"); } if (key.Length == 0) { throw new ArgumentException("key is an empty string"); } if (IsReadOnly) { throw new NotSupportedException("read-only dictionary"); } // updating version ++version; TstDictionaryEntry de = Find(key); if (de == null) { Add(key, value); } else { if (IsFixedSize) { throw new NotSupportedException("fixed-size dictionary"); } de.Value = value; } } }
/// <summary> /// Advances the enumerator to the next element of the collection. /// </summary> /// <returns> /// true if the enumerator was successfully advanced to the next element; /// false if the enumerator has passed the end of the collection. /// </returns> public bool MoveNext() { this.ThrowIfChanged(); // we are at the beginning if (stack == null) { stack = new Stack(); currentNode = null; if (dictionary.Root != null) { stack.Push(dictionary.Root); } } // we are at the end node, finished else if (currentNode == null) { throw new InvalidOperationException("out of range"); } if (stack.Count == 0) { currentNode = null; } while (stack.Count > 0) { currentNode = (TstDictionaryEntry)stack.Pop(); if (currentNode.HighChild != null) { stack.Push(currentNode.HighChild); } if (currentNode.EqChild != null) { stack.Push(currentNode.EqChild); } if (currentNode.LowChild != null) { stack.Push(currentNode.LowChild); } if (currentNode.IsKey) { break; } } return(currentNode != null); }
/// <summary> /// Traverses the <paramref name="p"/> sub-tree. /// </summary> /// <param name="p">node to traverse.</param> protected void Traverse(TstDictionaryEntry p) { if (p == null) { return; } OnTreeEntry(p); OnLowChild(p.LowChild); Traverse(p.LowChild); OnEqChild(p.EqChild); Traverse(p.EqChild); OnHighChild(p.HighChild); Traverse(p.HighChild); }
/// <summary> /// Creates a shallow copy of the entry /// </summary> /// <returns>entry shallow copy</returns> public Object Clone() { TstDictionaryEntry entry = new TstDictionaryEntry(Parent, SplitChar); if (LowChild != null) { entry.LowChild = LowChild.Clone() as TstDictionaryEntry; } if (EqChild != null) { entry.EqChild = EqChild.Clone() as TstDictionaryEntry; } if (HighChild != null) { entry.HighChild = HighChild.Clone() as TstDictionaryEntry; } return(entry); }
/// <summary> /// Finds the tst node matching the key. /// </summary> /// <returns>the <see cref="TstDictionaryEntry"/> mathcing the key, null if not found.</returns> /// <exception cref="ArgumentNullException"><paramref name="key"/> is null.</exception> public virtual TstDictionaryEntry Find(String key) { if (key == null) { throw new ArgumentNullException("key"); } int n = key.Length; if (n == 0) { return(null); } TstDictionaryEntry p = Root; int index = 0; char c; while (index < n && p != null) { c = key[index]; if (c < p.SplitChar) { p = p.LowChild; } else if (c > p.SplitChar) { p = p.HighChild; } else { if (index == n - 1) { return(p); } else { ++index; p = p.EqChild; } } } return(p); }
/// /// Returns alphabetical list of all keys in the tree that begin with prefix. /// /// The prefix. /// The number of values to return. /// public virtual ICollection PrefixMatch(string prefix, int numReturnValues) { if (prefix == null) { throw new ArgumentNullException("prefix"); } if (prefix.Length == 0) { throw new ArgumentException("prefix is empty"); } if (numReturnValues < -1 || numReturnValues == 0) { throw new ArgumentException("invalid numReturnValues"); } TstDictionaryEntry startNode = Find(prefix); if (startNode == null) { return(null); } int keysNumReturnValues = numReturnValues; StringCollection keyList = new StringCollection(); if (startNode.IsKey) { keyList.Add(startNode.Key); DecreaseRecursionCount(ref keysNumReturnValues); } if (startNode.EqChild != null && (keysNumReturnValues == -1 || keysNumReturnValues > 0)) { GetKeysRecursion(startNode.EqChild, ref keyList, ref keysNumReturnValues); } return(keyList); }
/// <summary> /// /// </summary> /// <param name="p"></param> /// <param name="key"></param> /// <param name="index"></param> /// <param name="wildChar"></param> /// <param name="matches"></param> internal void PartialMatchSearch( TstDictionaryEntry p, string key, int index, char wildChar, IList matches ) { if (p == null) { return; } char c = key[index]; if (c == wildChar || c < p.SplitChar) { PartialMatchSearch(p.LowChild, key, index, wildChar, matches); } if (c == wildChar || c == p.SplitChar) { if (index < key.Length - 1) { PartialMatchSearch(p.EqChild, key, index + 1, wildChar, matches); } else if (p.IsKey) { matches.Add(new DictionaryEntry(p.Key, p.Value)); } } if (c == wildChar || c > p.SplitChar) { PartialMatchSearch(p.HighChild, key, index, wildChar, matches); } }
///<summary> /// Adds an element with the specified key and value into the <see cref="TstDictionary"/>. ///</summary> /// <param name="key">The key of the element to add.</param> /// <param name="value">The value of the element to add. The value can be a null reference (Nothing in Visual Basic).</param> /// <exception cref="ArgumentNullException"> /// <paramref name="key"/> is a null reference (Nothing in Visual Basic). /// </exception> /// <exception cref="ArgumentException"><paramref name="key"/> is an empty string</exception> /// <exception cref="ArgumentException"> /// An element with the same key already exists in the <see cref="TstDictionary"/>. /// </exception> /// <exception cref="NotSupportedException">The <see cref="TstDictionary"/> is read-only.</exception> /// <exception cref="NotSupportedException">The <see cref="TstDictionary"/> has a fixed size.</exception> public virtual void Add(String key, Object value) { if (key == null) throw new ArgumentNullException("key is null"); if (key.Length == 0) throw new ArgumentException("trying to add empty key"); if (IsReadOnly) throw new NotSupportedException("dictionary is read-only"); if (IsFixedSize) throw new NotSupportedException("dictionary has fixed size"); // updating version ++version; // creating root node if needed. if (Root == null) root = new TstDictionaryEntry(null, key[0]); // adding key TstDictionaryEntry p = Root; int i = 0; char c; while (i < key.Length) { c = key[i]; if (c < p.SplitChar) { if (p.LowChild == null) p.LowChild = new TstDictionaryEntry(p, c); p = p.LowChild; continue; } if (c > p.SplitChar) { if (p.HighChild == null) p.HighChild = new TstDictionaryEntry(p, c); p = p.HighChild; continue; } else { ++i; if (i == key.Length) { if (p.IsKey) throw new ArgumentException("key already in dictionary"); break; } if (p.EqChild == null) p.EqChild = new TstDictionaryEntry(p, key[i]); p = p.EqChild; } } p.IsKey = true; p.Key = key; p.Value = value; }
///<summary> /// Removes the element with the specified key from the <see cref="TstDictionary"/>. /// </summary> /// <param name="key">The key of the element to remove.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="key"/> is a null reference (Nothing in Visual Basic). /// </exception> /// <exception cref="ArgumentException"><paramref name="key"/> is an empty string</exception> /// <exception cref="NotSupportedException">The <see cref="TstDictionary"/> is read-only.</exception> /// <exception cref="NotSupportedException">The <see cref="TstDictionary"/> has a fixed size.</exception> public virtual void Remove(String key) { if (key == null) throw new ArgumentNullException("key is null"); if (key.Length == 0) throw new ArgumentException("key length cannot be 0"); if (IsReadOnly) throw new NotSupportedException("dictionary is read-only"); if (IsFixedSize) throw new NotSupportedException("dictionary has fixed size"); // updating version ++version; TstDictionaryEntry p = Find(key); if (p == null) return; p.IsKey = false; p.Key = null; while (!p.IsKey && !p.HasChildren && p.Parent != null) { if (p.IsLowChild) p.Parent.LowChild = null; else if (p.IsHighChild) p.Parent.HighChild = null; else p.Parent.EqChild = null; p = p.Parent; } if (!p.IsKey && !p.HasChildren && p == root) root = null; }
/// <summary> /// Advances the enumerator to the next element of the collection. /// </summary> /// <returns> /// true if the enumerator was successfully advanced to the next element; /// false if the enumerator has passed the end of the collection. /// </returns> public bool MoveNext() { this.ThrowIfChanged(); // we are at the beginning if (stack == null) { stack = new Stack(); currentNode = null; if (dictionary.Root != null) stack.Push(dictionary.Root); } // we are at the end node, finished else if (currentNode == null) throw new InvalidOperationException("out of range"); if (stack.Count == 0) currentNode = null; while (stack.Count > 0) { currentNode = (TstDictionaryEntry)stack.Pop(); if (currentNode.HighChild != null) stack.Push(currentNode.HighChild); if (currentNode.EqChild != null) stack.Push(currentNode.EqChild); if (currentNode.LowChild != null) stack.Push(currentNode.LowChild); if (currentNode.IsKey) break; } return currentNode != null; }
/// <summary> /// Removes all elements from the <see cref="TstDictionary"/>. /// </summary> /// <exception cref="NotSupportedException">The <see cref="TstDictionary"/> is read-only.</exception> public virtual void Clear() { if (IsReadOnly) throw new NotSupportedException("dictionary is read-only"); // updating version ++version; root = null; }
/// <summary> /// /// </summary> /// <param name="p"></param> /// <param name="key"></param> /// <param name="index"></param> /// <param name="wildChar"></param> /// <param name="matches"></param> internal void PartialMatchSearch( TstDictionaryEntry p, string key, int index, char wildChar, IList matches ) { if (p == null) return; char c = key[index]; if (c == wildChar || c < p.SplitChar) PartialMatchSearch(p.LowChild, key, index, wildChar, matches); if (c == wildChar || c == p.SplitChar) { if (index < key.Length - 1) PartialMatchSearch(p.EqChild, key, index + 1, wildChar, matches); else if (p.IsKey) matches.Add(new DictionaryEntry(p.Key, p.Value)); } if (c == wildChar || c > p.SplitChar) PartialMatchSearch(p.HighChild, key, index, wildChar, matches); }
///<summary> /// Constructor ///</summary> /// <remarks> /// Construct an empty ternary search tree. /// </remarks> public TstDictionary() { root = null; version = 0; }
/// <summary> /// Creates a shallow copy of the entry /// </summary> /// <returns>entry shallow copy</returns> public Object Clone() { TstDictionaryEntry entry = new TstDictionaryEntry(Parent, SplitChar); if (LowChild != null) entry.LowChild = LowChild.Clone() as TstDictionaryEntry; if (EqChild != null) entry.EqChild = EqChild.Clone() as TstDictionaryEntry; if (HighChild != null) entry.HighChild = HighChild.Clone() as TstDictionaryEntry; return entry; }
internal void GetKeysRecursion(TstDictionaryEntry currentNode, ref StringCollection keyList, ref int keysNumReturnValues) { if (currentNode == null) return; if (currentNode.LowChild != null && (keysNumReturnValues == -1 || keysNumReturnValues > 0)) GetKeysRecursion(currentNode.LowChild, ref keyList, ref keysNumReturnValues); if (currentNode.IsKey && (keysNumReturnValues == -1 || keysNumReturnValues > 0)) { keyList.Add(currentNode.Key); DecreaseRecursionCount(ref keysNumReturnValues); } if (currentNode.EqChild != null && (keysNumReturnValues == -1 || keysNumReturnValues > 0)) GetKeysRecursion(currentNode.EqChild, ref keyList, ref keysNumReturnValues); if (currentNode.HighChild != null && (keysNumReturnValues == -1 || keysNumReturnValues > 0)) GetKeysRecursion(currentNode.HighChild, ref keyList, ref keysNumReturnValues); }
/// <summary> /// /// </summary> /// <param name="p"></param> /// <param name="key"></param> /// <param name="index"></param> /// <param name="dist"></param> /// <param name="matches"></param> internal void NearNeighborsSearch( TstDictionaryEntry p, string key, int index, int dist, IList matches ) { if (p == null || dist < 0) return; char c = key[index]; // low child if (dist > 0 || c < p.SplitChar) NearNeighborsSearch(p.LowChild, key, index, dist, matches); // eq child if (p.IsKey) { if (key.Length - index <= dist) matches.Add(new DictionaryEntry(p.Key, p.Value)); } else { int localIndex = index; if (localIndex != key.Length - 1) ++localIndex; int localDist = dist; if (c != p.SplitChar) --localDist; NearNeighborsSearch( p.EqChild, key, localIndex, localDist, matches ); } // highchild if (dist > 0 || c > p.SplitChar) NearNeighborsSearch(p.HighChild, key, index, dist, matches); }
/// <summary> /// Traverses the <paramref name="p"/> sub-tree. /// </summary> /// <param name="p">node to traverse.</param> protected void Traverse(TstDictionaryEntry p) { if (p == null) return; OnTreeEntry(p); OnLowChild(p.LowChild); Traverse(p.LowChild); OnEqChild(p.EqChild); Traverse(p.EqChild); OnHighChild(p.HighChild); Traverse(p.HighChild); }
///<summary> /// Adds an element with the specified key and value into the <see cref="TstDictionary"/>. ///</summary> /// <param name="key">The key of the element to add.</param> /// <param name="value">The value of the element to add. The value can be a null reference (Nothing in Visual Basic).</param> /// <exception cref="ArgumentNullException"> /// <paramref name="key"/> is a null reference (Nothing in Visual Basic). /// </exception> /// <exception cref="ArgumentException"><paramref name="key"/> is an empty string</exception> /// <exception cref="ArgumentException"> /// An element with the same key already exists in the <see cref="TstDictionary"/>. /// </exception> /// <exception cref="NotSupportedException">The <see cref="TstDictionary"/> is read-only.</exception> /// <exception cref="NotSupportedException">The <see cref="TstDictionary"/> has a fixed size.</exception> public virtual void Add(String key, Object value) { if (key == null) { throw new ArgumentNullException("key is null"); } if (key.Length == 0) { throw new ArgumentException("trying to add empty key"); } if (IsReadOnly) { throw new NotSupportedException("dictionary is read-only"); } if (IsFixedSize) { throw new NotSupportedException("dictionary has fixed size"); } // updating version ++version; // creating root node if needed. if (Root == null) { root = new TstDictionaryEntry(null, key[0]); } // adding key TstDictionaryEntry p = Root; int i = 0; char c; while (i < key.Length) { c = key[i]; if (c < p.SplitChar) { if (p.LowChild == null) { p.LowChild = new TstDictionaryEntry(p, c); } p = p.LowChild; continue; } if (c > p.SplitChar) { if (p.HighChild == null) { p.HighChild = new TstDictionaryEntry(p, c); } p = p.HighChild; continue; } else { ++i; if (i == key.Length) { if (p.IsKey) { throw new ArgumentException("key already in dictionary"); } break; } if (p.EqChild == null) { p.EqChild = new TstDictionaryEntry(p, key[i]); } p = p.EqChild; } } p.IsKey = true; p.Key = key; p.Value = value; }
/// <summary>Create a <see cref="TstDictionaryEntry"/> event argument.</summary> /// <param name="entry">A <see cref="TstDictionaryEntry"/> entry to pass as argument.</param> public TstDictionaryEntryEventArgs(TstDictionaryEntry entry) { this.entry = entry; }