Click here to Skip to main content
15,881,173 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 37K   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 Loyc.Utilities;
using System.Diagnostics;
using Loyc.Collections;

namespace Loyc.Collections.Impl
{
	/// <summary>This CPTrie "bitmap" node splits the 8-bit alphabet space into 8
	/// buckets of 5 bytes each; a CPSNode is used to store the keys in each
	/// bucket.</summary>
	/// <typeparam name="T">Type of values associated with each key</typeparam>
	class CPBNode<T> : CPNode<T>
	{
		CPSNode<T>[] _children = new CPSNode<T>[8];

		static readonly object NoZLK = new object();
		/// <summary>The value associated with a zero-length key, if any, is stored
		/// here directly rather than in any of the children.</summary>
		object _zlk = NoZLK;
		
		int _localCount = 0;

		public CPBNode() {}
		public CPBNode(CPBNode<T> copy)
		{
			for (int i = 0; i < _children.Length; i++)
				if (copy._children[i] != null)
					_children[i] = (CPSNode<T>)copy._children[i].CloneAndOptimize();
			
			_zlk = copy._zlk;
			_localCount = copy._localCount;
		}

		public override bool Find(ref KeyWalker key, CPEnumerator<T> e)
		{
			if (key.Left == 0) {
				MoveFirst(e);
				return _zlk != NoZLK;
			} else {
				int i = key[0] >> 5;
				e.Stack.Add(new CPEnumerator<T>.Entry(this, i, e.Key.Offset));
				if (_children[i] != null)
					return _children[i].Find(ref key, e);
				else {
					e.MoveNext();
					return false;
				}
			}
		}

		public override bool Set(ref KeyWalker key, ref T value, ref CPNode<T> self, CPMode mode)
		{
			CheckValidity();

			if (key.Left > 0)
			{
				int i = key[0] >> 5;
				if (_children[i] != null) {
					CPSNode<T> child = _children[i];
					CPNode<T> child2 = child;
					_localCount -= child.LocalCount;
					bool existed = child.Set(ref key, ref value, ref child2, mode | CPMode.FixedStructure);
					_localCount += child.LocalCount;
					Debug.Assert(child == child2);
					return existed;
				} else {
					if ((mode & CPMode.Create) != (CPMode)0) {
						_children[i] = new CPSNode<T>(ref key, value);
						_localCount++;
					}
					return false;
				}
			}
			else
			{
				// key.Left == 0
				if (_zlk == NoZLK)
				{
					if ((mode & CPMode.Create) != (CPMode)0) {
						_zlk = value;
						_localCount++;
					}
					return false;
				}
				else
				{
					T oldValue = (T)_zlk;
					if ((mode & CPMode.Set) != (CPMode)0)
						_zlk = value;
					value = oldValue;
					return true;
				}
			}
		}

		public override void AddChild(ref KeyWalker key, CPNode<T> value, ref CPNode<T> self)
		{
			Debug.Assert(key.Left > 0);
			int i = key[0] >> 5;
			if (_children[i] != null) {
				CPSNode<T> child = _children[i];
				CPNode<T> child2 = child;
				_localCount -= child.LocalCount;
				child.AddChild(ref key, value, ref child2);
				_localCount += child.LocalCount;				
				Debug.Assert(child2 == child);
			} else {
				_children[i] = new CPSNode<T>(ref key, value);
				_localCount++;
			}
		}

		public override bool Remove(ref KeyWalker key, ref T oldValue, ref CPNode<T> self)
		{
			CheckValidity();

			if (key.Left > 0)
			{
				int i = key[0] >> 5;
				if (_children[i] != null) {
					CPSNode<T> child = _children[i];
					CPNode<T> child2 = child;
					int childLCount = child.LocalCount;
					if (child.Remove(ref key, ref oldValue, ref child2)) {
						_localCount -= childLCount;
						if (child2 == null)
							_children[i] = null;
						else {
							_localCount += child.LocalCount;
							Debug.Assert(child == child2);
						}
						if (_localCount < 24)
							ConvertToSNode(ref self);
						return true;
					}
					Debug.Assert(child == _children[i]);
				}
				return false;
			}
			else
			{
				// key.Left == 0
				if (_zlk == NoZLK)
					return false;
				else {
					oldValue = (T)_zlk;
					_zlk = NoZLK;
					_localCount--;
					if (IsEmpty)
						self = null;
					return true;
				}
			}
		}

		[Conditional("DEBUG")]
		private void CheckValidity()
		{
			int count = 0; // debug check
			for (int i = 0; i < _children.Length; i++)
				if (_children[i] != null)
					count += _children[i].LocalCount;
			if (_zlk != NoZLK)
				count++;
			Debug.Assert(count == _localCount);
		}

		private void ConvertToSNode(ref CPNode<T> self)
		{
			Debug.Assert(_localCount <= 32);
			int count = 0; // debug check
			self = new CPSNode<T>(_localCount);
			for (int i = 0; i < _children.Length; i++)
				if (_children[i] != null) {
					count += _children[i].LocalCount;
					_children[i].MoveAllTo(self);
				}
			if (_zlk != NoZLK) {
				KeyWalker kw = new KeyWalker(InternalList<byte>.EmptyArray, 0);
				T value = (T)_zlk;
				G.Verify(!self.Set(ref kw, ref value, ref self, CPMode.Create));
				count++;
			}
			Debug.Assert(count == _localCount);
		}

		private bool IsEmpty
		{
			get { return _localCount == 0; }
		}

		public override int CountMemoryUsage(int sizeOfT)
		{
			int size = 16 + 16 + _children.Length * 4;
			for (int i = 0; i < _children.Length; i++)
				if (_children[i] != null)
					size += _children[i].CountMemoryUsage(sizeOfT);

			if (_zlk != NoZLK && _zlk != null)
				size += 8 + sizeOfT;

			return size;	
		}
		public override CPNode<T> CloneAndOptimize()
		{
			return new CPBNode<T>(this);
		}

		public override int LocalCount
		{
			get { return _localCount; }
		}

		public override void MoveFirst(CPEnumerator<T> e)
		{
			if (_zlk != NoZLK)
			{
				e.Stack.Add(new CPEnumerator<T>.Entry(this, -1, e.Key.Offset));
				return;
			}
			for (int i = 0; i < _children.Length; i++)
				if (_children[i] != null) {
					e.Stack.Add(new CPEnumerator<T>.Entry(this, i, e.Key.Offset));
					_children[i].MoveFirst(e);
					return;
				}
		}
		public override void MoveLast(CPEnumerator<T> e)
		{
			for (int i = _children.Length - 1; i >= 0; i--)
				if (_children[i] != null)
				{
					e.Stack.Add(new CPEnumerator<T>.Entry(this, i, e.Key.Offset));
					_children[i].MoveLast(e);
					return;
				}
			Debug.Assert(_zlk != NoZLK);
			e.Stack.Add(new CPEnumerator<T>.Entry(this, -1, e.Key.Offset));
			return;
		}
		public override bool MoveNext(CPEnumerator<T> e)
		{
			int top = e.Stack.Count - 1;
			Debug.Assert(e.Stack[top].Node == this);
			for (int i = e.Stack[top].Index + 1; i < _children.Length; i++)
				if (_children[i] != null)
				{
					e.Stack.InternalArray[top].Index = i;
					_children[i].MoveFirst(e);
					return true;
				}
			return false;
		}
		public override bool MovePrev(CPEnumerator<T> e)
		{
			int top = e.Stack.Count - 1;
			Debug.Assert(e.Stack[top].Node == this);
			for (int i = e.Stack[top].Index - 1; i >= 0; i--)
				if (_children[i] != null)
				{
					e.Stack.InternalArray[top].Index = i;
					_children[i].MoveLast(e);
					return true;
				}
			if (e.Stack[top].Index > -1 && _zlk != NoZLK)
			{
				e.Stack.InternalArray[top].Index = -1;
				return true;
			}
			return false;
		}
	}
}

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