Click here to Skip to main content
15,885,767 members
Articles / Programming Languages / C#

The List Trifecta, Part 1

Rate me:
Please Sign up or sign in to vote.
4.97/5 (21 votes)
20 May 2016LGPL321 min read 37.1K   161   40  
The A-list is an all-purpose list, a data structure that can support most standard list operation in O(log n) time and does lots of other stuff, too
// http://www.codeproject.com/KB/recipes/cptrie.aspx
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using Loyc.Essentials;
using Loyc.Collections.Impl;

namespace Loyc.Collections
{
	/// <summary>A compact patricia trie that uses strings as keys.</summary>
	/// <typeparam name="TValue">Type of value associated with each key.</typeparam>
	public class CPStringTrie<TValue> : CPTrie<TValue>, IDictionary<string, TValue>
	{
		public CPStringTrie() { }
		public CPStringTrie(CPStringTrie<TValue> clone) : base(clone) { }

		public new int CountMemoryUsage(int sizeOfT) { return base.CountMemoryUsage(sizeOfT); }

		#region IDictionary<string,TValue> Members

		public void Add(string key, TValue value)
		{
			KeyWalker kw = StringToBytes(key);
			if (base.Set(ref kw, ref value, CPMode.Create))
				throw new ArgumentException(Localize.From("Key already exists: ") + key);
		}

		/// <summary>Adds the specified key-value pair only if the specified key is
		/// not already present in the trie.</summary>
		/// <returns>Returns true if the key-value pair was added or false if
		/// the key already existed. In the false case, the trie is not modified.</returns>
		public bool TryAdd(string key, TValue value)
		{
			KeyWalker kw = StringToBytes(key);
			return !base.Set(ref kw, ref value, CPMode.Set);
		}
		/// <summary>Adds the specified key-value pair only if the specified key is
		/// not already present in the trie.</summary>
		/// <param name="value">On entry, value specifies the value to associate
		/// with the specified key, but if the key already exists, value is changed
		/// to the value associated with the existing key.</param>
		/// <returns>Returns true if the key-value pair was added or false if
		/// the key already existed. In the false case, the trie is not modified.</returns>
		public bool TryAdd(string key, ref TValue value)
		{
			KeyWalker kw = StringToBytes(key);
			return !base.Set(ref kw, ref value, CPMode.Create);
		}

		public bool ContainsKey(string key)
		{
			KeyWalker kw = StringToBytes(key);
			TValue value = default(TValue);
			return base.Find(ref kw, ref value);
		}

		public bool Remove(string key)
		{
			KeyWalker kw = StringToBytes(key);
			TValue oldValue = default(TValue);
			return base.Remove(ref kw, ref oldValue);
		}
		public bool Remove(string key, ref TValue value)
		{
			KeyWalker kw = StringToBytes(key);
			return base.Remove(ref kw, ref value);
		}

		public bool TryGetValue(string key, out TValue value)
		{
			KeyWalker kw = StringToBytes(key);
			value = default(TValue);
			return base.Find(ref kw, ref value);
		}

		public TValue this[string key, TValue defaultValue]
		{
			get {
				KeyWalker kw = StringToBytes(key);
				base.Find(ref kw, ref defaultValue);
				return defaultValue;
			}
		}
		public TValue this[string key]
		{
			get {
				KeyWalker kw = StringToBytes(key);
				TValue value = default(TValue);
				if (!base.Find(ref kw, ref value))
					throw new KeyNotFoundException(Localize.From("Key not found: ") + key);
				return value;
			}
			set {
				KeyWalker kw = StringToBytes(key);
				base.Set(ref kw, ref value, CPMode.Set | CPMode.Create);
			}
		}

		public ICollection<string> Keys
		{
			get { return new KeyCollection(this); }
		}
		public ICollection<TValue> Values
		{
			get { return new CPValueCollection<TValue>(this); }
		}

		public void Add(KeyValuePair<string, TValue> item)
		{
			Add(item.Key, item.Value);
		}

		public new void Clear()
		{
			base.Clear();
		}

		public bool Contains(KeyValuePair<string, TValue> item)
		{
			KeyWalker kw = StringToBytes(item.Key);
			TValue value = default(TValue);
			if (base.Find(ref kw, ref value))
				return DefaultComparer.Compare(value, item.Value) == 0;
			return false;
		}

		public void CopyTo(KeyValuePair<string, TValue>[] array, int arrayIndex)
		{
			foreach (KeyValuePair<string, TValue> pair in this)
				array[arrayIndex++] = pair;
		}

		public new int Count
		{
			get { return base.Count; }
		}

		public bool IsReadOnly
		{
			get { return false; }
		}

		public bool Remove(KeyValuePair<string, TValue> item)
		{
			KeyWalker kw = StringToBytes(item.Key);
			KeyWalker kw2 = kw;
			TValue value = default(TValue);
			if (Find(ref kw, ref value) && DefaultComparer.Compare(value, item.Value) == 0)
				return Remove(ref kw2, ref value);
			return false;
		}

		#endregion

		#region IEnumerable<KeyValuePair<string,TValue>> Members

		public Enumerator GetEnumerator()
		{
			return new Enumerator(this);
		}
		IEnumerator<KeyValuePair<string, TValue>> IEnumerable<KeyValuePair<string, TValue>>.GetEnumerator()
		{
			return GetEnumerator();
		}
		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		#endregion

		#region Find methods

		public Enumerator FindAtLeast(string key)
		{
			KeyWalker kw = StringToBytes(key);
			Enumerator e = new Enumerator(this);
			base.Find(ref kw, e);
			return e;
		}
		public Enumerator FindExact(string key)
		{
			KeyWalker kw = StringToBytes(key);
			Enumerator e = new Enumerator(this);
			if (!base.Find(ref kw, e))
				return null;
			Debug.Assert(e.IsValid);
			return e;
		}
		public bool Find(string key, out Enumerator e)
		{
			KeyWalker kw = StringToBytes(key);
			e = new Enumerator(this);
			return base.Find(ref kw, e);
		}

		#endregion

		public CPStringTrie<TValue> Clone()
			{ return new CPStringTrie<TValue>(this); }

		public bool IsEmpty { get { return base.Count == 0; } }

		#region Enumerator and KeyEnumerator
		// CPEnumerator<TValue> is the corresponding value enumerator

		/// <summary>Enumerates key-value pairs in a CPStringTrie.</summary>
		/// <remarks>Reading the key is more expensive than reading the value
		/// because the key must be decoded from the bytes it is made up of.
		/// If you call CurrentValue instead of Current or CurrentKey, the
		/// work of decoding the key will be avoided. If you only need to
		/// enumerate the values, enumerate the Values collection instead of 
		/// the trie class itself.
		/// </remarks>
		public class Enumerator : CPEnumerator<TValue>, IEnumerator<KeyValuePair<string, TValue>>
		{
			internal protected Enumerator(CPTrie<TValue> trie) : base(trie) {}

			public new KeyValuePair<string, TValue> Current
			{
				get {
					return new KeyValuePair<string, TValue>(CurrentKey, CurrentValue);
				}
			}
			object System.Collections.IEnumerator.Current
			{
				get { return Current; }
			}
			public new TValue CurrentValue
			{
				get { return base.CurrentValue; }
			}
			public new string CurrentKey
			{
				get {
					return CPTrie<TValue>.BytesToString(Key.Buffer, Key.Offset + Key.Left);
				}
			}
		}

		/// <summary>Enumerates keys of a CPStringTrie.</summary>
		/// <remarks>
		/// Avoid calling Current more than once per key, as each call requires the
		/// key to be decoded from the bytes it is made up of.
		/// </remarks>
		public class KeyEnumerator : CPEnumerator<TValue>, IEnumerator<string>
		{
			internal protected KeyEnumerator(CPTrie<TValue> trie) : base(trie) {}

			public new string Current
			{
				get { return CPTrie<TValue>.BytesToString(Key.Buffer, Key.Offset + Key.Left); }
			}
			object System.Collections.IEnumerator.Current
			{
				get { return Current; }
			}
		}

		#endregion

		#region KeyCollection
		// CPValueCollection<TValue> is the corresponding value collection

		public class KeyCollection : ICollection<string>
		{
			CPTrie<TValue> _trie;
			public KeyCollection(CPTrie<TValue> trie) { _trie = trie; }

			#region ICollection<string> Members

			public void Add(string item)    { throw new NotSupportedException(); }
			public void Clear()             { throw new NotSupportedException(); }
			public bool Remove(string item) { throw new NotSupportedException(); }
			public bool IsReadOnly          { get { return true; } }
			public int Count                { get { return _trie.Count; } }

			public bool Contains(string item)
			{
				foreach (string value in this)
					if (value == item)
						return true;
				return false;
			}

			public void CopyTo(string[] array, int arrayIndex)
			{
				if (arrayIndex < 0)
					throw new ArgumentOutOfRangeException();
				if (arrayIndex + _trie.Count > array.Length)
					throw new ArgumentException();
				foreach (string value in this)
					array[arrayIndex++] = value;
			}

			public KeyEnumerator GetEnumerator()
			{
				return new KeyEnumerator(_trie);
			}
			IEnumerator<string> IEnumerable<string>.GetEnumerator()
			{
				return GetEnumerator();
			}
			System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
			{
				return GetEnumerator();
			}

			#endregion
		}

		#endregion
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Software Developer None
Canada Canada
Since I started programming when I was 11, I wrote the SNES emulator "SNEqr", the FastNav mapping component, the Enhanced C# programming language (in progress), the parser generator LLLPG, and LES, a syntax to help you start building programming languages, DSLs or build systems.

My overall focus is on the Language of your choice (Loyc) initiative, which is about investigating ways to improve interoperability between programming languages and putting more power in the hands of developers. I'm also seeking employment.

Comments and Discussions