Click here to Skip to main content
15,884,693 members
Articles / Programming Languages / C# 4.0

The List Trifecta, Part 2

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
7 Sep 2013LGPL310 min read 28.6K   316   12  
The BDictionary is like a Dictionary mashed up with List<T>. BList and BMultiMap also say hello.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Loyc.Collections;

namespace Loyc.Collections.Impl
{
	[TestFixture]
	public class BListTests : AListTestBase<BList<int>, int>
	{
		int _maxInnerSize, _maxLeafSize;

		public BListTests() : this(true) { }
		public BListTests(bool testExceptions) : this(testExceptions, Environment.TickCount, BListLeaf<int,int>.DefaultMaxNodeSize, BListInner<int,int>.DefaultMaxNodeSize) { }
		public BListTests(bool testExceptions, int randomSeed, int maxLeafSize, int maxInnerSize) 
			: base(testExceptions, randomSeed)
		{
			_maxInnerSize = maxInnerSize;
			_maxLeafSize = maxLeafSize;
		}

		#region Implementations of abstract methods

		protected override BList<int> NewList()
		{
			return new BList<int>(_maxLeafSize, _maxInnerSize);
		}
		protected override int AddToBoth(BList<int> blist, List<int> list, int item, int preferredIndex)
		{
			int i = Add(blist, item, preferredIndex);
			list.Insert(i, item);
			return i;
		}
		protected override int Add(BList<int> blist, int item, int preferredIndex)
		{
			int i = blist.FindLowerBound(item);
			blist.Add(item);
			return i;
		}
		protected override BList<int> CopySection(BList<int> blist, int start, int subcount)
		{
			return blist.CopySection(start, subcount);
		}
		protected override BList<int> RemoveSection(BList<int> blist, int start, int subcount)
		{
			return blist.RemoveSection(start, subcount);
		}
		protected override bool RemoveFromBoth(BList<int> blist, List<int> list, int item)
		{
			int i = blist.IndexOf(item);
			if (i == -1)
				return false;
			blist.Remove(item);
			list.RemoveAt(i);
			return true;
		}
		protected override int GetKey(int item)
		{
			return item;
		}

		#endregion

		[Test]
		public void TestStandardOperations()
		{
			List<int> list = new List<int>();
			BList<int> blist = NewList();

			// Ensure standard operations work for various list sizes
			int next = 0, item;
			for (int size = 5; size <= 125; size *= 5)
			{
				while (blist.Count < size)
				{
					AddToBoth(blist, list, next += 2, -1);
					Assert.AreEqual(list.Count, blist.Count);
				}

				ExpectList(blist, list, _r.Next(2) == 0);

				// Add one in the middle
				int at = blist.FindLowerBound(size);
				blist.Do(AListOperation.Add, size);
				list.Insert(at, size);

				// Remove in different ways
				item = _r.Next(size * 2);
				Assert.AreEqual(list.Remove(item), blist.Remove(item));
				item = _r.Next(size * 2);
				Assert.AreEqual(list.Remove(item), blist.Do(AListOperation.Remove, item) == -1);

				// IndexOf/Contains
				item = _r.Next(size * 2);
				Assert.AreEqual(list.IndexOf(item), blist.IndexOf(item));
				item = _r.Next(size * 2);
				Assert.AreEqual(list.Contains(item), blist.Contains(item));

				ExpectList(blist, list, _r.Next(2) == 0);

				if (_testExceptions)
					AssertThrows<KeyAlreadyExistsException>(() => blist.Do(AListOperation.AddOrThrow, blist.First));

				// Replace an item with itself (with ints, this command can never 
				// do anything, but we'll try it in for completeness). Also,
				// try no-op AddOrReplace and AddIfNotPresent operations to verify
				// that they do nothing.
				Assert.AreEqual(0, blist.Do(AListOperation.ReplaceIfPresent, _r.Next(size)));
				Assert.AreEqual(0, blist.Do(AListOperation.AddOrReplace, blist.Last));
				Assert.AreEqual(0, blist.Do(AListOperation.AddIfNotPresent, blist.First));
				
				// Also do an AddOrReplace and AddIfNotPresent operation with an 
				// item that does not already exist.
				blist.Do(AListOperation.AddOrReplace, next + 3); // end of the list
				blist.Do(AListOperation.AddIfNotPresent, next + 1);
				blist.Do(AListOperation.Add, next + 1);
				list.Add(next + 1);
				list.Add(next + 1);
				list.Add(next + 3);
				
				ExpectList(blist, list, _r.Next(2) == 0);

				Assert.AreEqual(2, blist.RemoveAll(next + 1));
				Assert.IsTrue(list.Remove(next + 1));
				Assert.IsTrue(list.Remove(next + 1));
			}
		}

		[Test]
		public void TestRangeOperations()
		{
			BList<int> blist = NewList();
			var primes = new int[] { 2, 3, 5, 7, 11, 13, 17, 23 };
			blist.AddRange(new int[] { });
			blist.AddRange(primes);
			blist.AddRange(new int[] { });
			ExpectList(blist, primes);

			Assert.AreEqual(4, blist.AddRange(new int[] { 9, 9, 29, 9 }));
			ExpectList(blist, 2, 3, 5, 7, 9, 9, 9, 11, 13, 17, 23, 29);

			Assert.AreEqual(2, blist.RemoveRange(new int[] { 9, 9 }));
			ExpectList(blist, 2, 3, 5, 7, 9, 11, 13, 17, 23, 29);

			Assert.AreEqual(2, blist.RemoveRange(new int[] { 9, 9, 29, 9 }));
			ExpectList(blist, 2, 3, 5, 7, 11, 13, 17, 23);
		}

		[Test]
		public void TestUpperAndLowerBound()
		{
			BList<int> blist = NewList();
			blist.AddRange(new int[] { 0, 0, 10, 10, 20, 20, 25, 30, 30, 40, 40, 50, 50 });

			int item = 25;
			Assert.AreEqual(blist.FindUpperBound(item), 1 + blist.FindLowerBound(item));
			
			item = 10;
			Assert.That(blist.FindUpperBound(ref item) == 4 && item == 20);
			item = 0;
			Assert.That(blist.FindUpperBound(ref item) == 2 && item == 10);
			item = 999;
			Assert.That(blist.FindUpperBound(ref item) == blist.Count && item == 999);
			
			bool found;
			item = 5;
			Assert.That(blist.FindLowerBound(ref item, out found) == 2 && item == 10 && !found);
			item = 20;
			Assert.That(blist.FindLowerBound(ref item, out found) == 4 && item == 20 && found);
			item = 0;
			Assert.That(blist.FindLowerBound(ref item, out found) == 0 && item == 0 && found);
			item = 999;
			Assert.That(blist.FindLowerBound(ref item, out found) == blist.Count && item == 999 && !found);
		}
	}
}

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