PageRenderTime 322ms CodeModel.GetById 225ms app.highlight 76ms RepoModel.GetById 1ms app.codeStats 2ms

/Utilities/Collections/MultiDictionary.cs

#
C# | 2291 lines | 1653 code | 202 blank | 436 comment | 89 complexity | d951e5144ba674242e5bfff8b84ef410 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1using System;
   2using System.Collections.Generic;
   3using Delta.Utilities.Helpers;
   4using NUnit.Framework;
   5
   6namespace Delta.Utilities.Collections
   7{
   8	/// <summary>
   9	/// <para>The MultiDictionary class that associates values with a key.
  10	/// Unlike an Dictionary, each key can have multiple values associated with
  11	/// it. When indexing an MultiDictionary, instead of a single value
  12	/// associated with a key, you retrieve an enumeration of values.</para>
  13	/// <para>When constructed, you can chose to allow the same value to be
  14	/// associated with a key multiple times, or only one time. </para>
  15	/// </summary>
  16	/// <typeparam name="TKey">Key type</typeparam>
  17	/// <typeparam name="TValue">Value type</typeparam>
  18	/// <seealso cref="Dictionary{TKey,TValue}"/>
  19	public class MultiDictionary<TKey, TValue>
  20		: MultiDictionaryBase<TKey, TValue>,
  21		  ICloneable<MultiDictionary<TKey, TValue>>
  22	{
  23		#region KeyAndValues Struct
  24		/// <summary>
  25		/// A structure to hold the key and the values associated with the key.
  26		/// The number of values must always be 1 or greater in a version that is
  27		/// stored, but can be zero in a dummy version used only for lookups.
  28		/// </summary>
  29		private struct KeyAndValues
  30		{
  31			#region Copy (Static)
  32			/// <summary>
  33			/// Make a copy of a KeyAndValues, copying the array.
  34			/// </summary>
  35			/// <param name="x">KeyAndValues to copy.</param>
  36			/// <returns>A copied version.</returns>
  37			public static KeyAndValues Copy(KeyAndValues x)
  38			{
  39				KeyAndValues result;
  40
  41				result.Key = x.Key;
  42				result.Count = x.Count;
  43
  44				if (x.Values != null)
  45				{
  46					result.Values = (TValue[])x.Values.Clone();
  47				}
  48				else
  49				{
  50					result.Values = null;
  51				}
  52
  53				return result;
  54			}
  55			#endregion
  56
  57			#region Key (Public)
  58			/// <summary>
  59			/// The key.
  60			/// </summary>
  61			public TKey Key;
  62			#endregion
  63
  64			#region Count (Public)
  65			/// <summary>
  66			/// The number of values. Always at least 1 except in a dummy version
  67			/// for lookups.
  68			/// </summary>
  69			public int Count;
  70			#endregion
  71
  72			#region Values (Public)
  73			/// <summary>
  74			/// An array of values. 
  75			/// </summary>
  76			public TValue[] Values;
  77			#endregion
  78
  79			#region Constructors
  80			/// <summary>
  81			/// Create a dummy KeyAndValues with just the key, for lookups.
  82			/// </summary>
  83			/// <param name="key">The key to use.</param>
  84			public KeyAndValues(TKey key)
  85			{
  86				Key = key;
  87				Count = 0;
  88				Values = null;
  89			}
  90			#endregion
  91		}
  92		#endregion
  93
  94		#region KeyAndValuesEqualityComparer Class
  95		/// <summary>
  96		/// This class implements IEqualityComparer for KeysAndValues, allowing
  97		/// them to be compared by their keys. An IEqualityComparer on keys is
  98		/// required.
  99		/// </summary>
 100		private class KeyAndValuesEqualityComparer : IEqualityComparer<KeyAndValues>
 101		{
 102			#region Private
 103
 104			#region keyEqualityComparer (Private)
 105			/// <summary>
 106			/// Key equality comparer
 107			/// </summary>
 108			private readonly IEqualityComparer<TKey> keyEqualityComparer;
 109			#endregion
 110
 111			#endregion
 112
 113			#region Constructors
 114			/// <summary>
 115			/// Create key and values equality comparer
 116			/// </summary>
 117			/// <param name="keyEqualityComparer">Key equality comparer</param>
 118			public KeyAndValuesEqualityComparer(
 119				IEqualityComparer<TKey> keyEqualityComparer)
 120			{
 121				this.keyEqualityComparer = keyEqualityComparer;
 122			}
 123			#endregion
 124
 125			#region IEqualityComparer<MultiDictionary<TKey,TValue>.KeyAndValues> Members
 126			/// <summary>
 127			/// Equals
 128			/// </summary>
 129			/// <param name="x">X value</param>
 130			/// <param name="y">Y value</param>
 131			/// <returns>True if x and y match.</returns>
 132			public bool Equals(KeyAndValues x, KeyAndValues y)
 133			{
 134				return keyEqualityComparer.Equals(x.Key, y.Key);
 135			}
 136
 137			/// <summary>
 138			/// Get hash code
 139			/// </summary>
 140			/// <param name="obj">Object to generate hash code for</param>
 141			/// <returns>Hash code</returns>
 142			public int GetHashCode(KeyAndValues obj)
 143			{
 144				return obj.Key == null
 145				       	? 0x1786E23C
 146				       	: keyEqualityComparer.GetHashCode(obj.Key);
 147			}
 148			#endregion
 149		}
 150		#endregion
 151
 152		#region GetHashCode (Static)
 153		/// <summary>
 154		/// Gets the hash code for an object using a comparer. Correctly handles
 155		/// null.
 156		/// </summary>
 157		/// <param name="item">Item to get hash code for. Can be null.</param>
 158		/// <param name="equalityComparer">The comparer to use.</param>
 159		/// <returns>The hash code for the item.</returns>
 160		public static int GetHashCode<T>(T item,
 161			IEqualityComparer<T> equalityComparer)
 162		{
 163			if (item == null)
 164			{
 165				return 0x1786E23C;
 166			}
 167			else
 168			{
 169				return equalityComparer.GetHashCode(item);
 170			}
 171		}
 172		#endregion
 173
 174		#region IsCloneableType (Static)
 175		/// <summary>
 176		/// Determine if a type is cloneable: either a value type or implementing
 177		/// ICloneable.
 178		/// </summary>
 179		/// <param name="type">Type to check.</param>
 180		/// <param name="isValue">Returns if the type is a value type, and does not
 181		/// implement ICloneable.</param>
 182		/// <returns>True if the type is cloneable.</returns>
 183		public static bool IsCloneableType(Type type, out bool isValue)
 184		{
 185			isValue = false;
 186
 187			if (typeof(ICloneable<MultiDictionary<TKey, TValue>>).
 188				IsAssignableFrom(type))
 189			{
 190				return true;
 191			}
 192			else if (type.IsValueType)
 193			{
 194				isValue = true;
 195				return true;
 196			}
 197			else
 198			{
 199				return false;
 200			}
 201		}
 202		#endregion
 203
 204		#region KeyComparer (Public)
 205		/// <summary>
 206		/// Returns the IEqualityComparer&lt;T&gt; used to compare keys in this
 207		/// dictionary.
 208		/// </summary>
 209		/// <value>If the dictionary was created using a comparer, that comparer
 210		/// is returned. Otherwise the default comparer for TKey
 211		/// (EqualityComparer&lt;TKey&gt;.Default) is returned.</value>
 212		/// <typeparam name="TKey">TKey</typeparam>
 213		/// <typeparam name="TValue">TValue</typeparam>
 214		public IEqualityComparer<TKey> KeyComparer
 215		{
 216			get
 217			{
 218				return keyEqualityComparer;
 219			}
 220		}
 221		#endregion
 222
 223		#region ValueComparer (Public)
 224		/// <summary>
 225		/// Returns the IEqualityComparer&lt;T&gt; used to compare values in this
 226		/// dictionary.
 227		/// </summary>
 228		/// <value>If the dictionary was created using a comparer, that comparer
 229		/// is returned. Otherwise the default comparer for TValue
 230		/// (EqualityComparer&lt;TValue&gt;.Default) is returned.</value>
 231		/// <typeparam name="TKey">TKey</typeparam>
 232		/// <typeparam name="TValue">TValue</typeparam>
 233		public IEqualityComparer<TValue> ValueComparer
 234		{
 235			get
 236			{
 237				return valueEqualityComparer;
 238			}
 239		}
 240		#endregion
 241
 242		#region Count (Public)
 243		/// <summary>
 244		/// Gets the number of key-value pairs in the dictionary. Each value
 245		/// associated with a given key is counted. If duplicate values are
 246		/// permitted, each duplicate value is included in the count.
 247		/// </summary>
 248		/// <value>The number of key-value pairs in the dictionary.</value>
 249		/// <typeparam name="TKey">TKey</typeparam>
 250		/// <typeparam name="TValue">TValue</typeparam>
 251		public override sealed int Count
 252		{
 253			get
 254			{
 255				return hash.ElementCount;
 256			}
 257		}
 258		#endregion
 259
 260		#region Private
 261
 262		#region keyEqualityComparer (Private)
 263		/// <summary>
 264		/// The comparer for comparing keys
 265		/// </summary>
 266		/// <typeparam name="TKey">TKey</typeparam>
 267		/// <typeparam name="TValue">TValue</typeparam>
 268		private readonly IEqualityComparer<TKey> keyEqualityComparer;
 269		#endregion
 270
 271		#region valueEqualityComparer (Private)
 272		/// <summary>
 273		/// The comparer for comparing values;
 274		/// </summary>
 275		/// <typeparam name="TKey">TKey</typeparam>
 276		/// <typeparam name="TValue">TValue</typeparam>
 277		private readonly IEqualityComparer<TValue> valueEqualityComparer;
 278		#endregion
 279
 280		#region equalityComparer (Private)
 281		/// <summary>
 282		/// The comparer for compaing keys and values.
 283		/// </summary>
 284		/// <typeparam name="TKey">TKey</typeparam>
 285		/// <typeparam name="TValue">TValue</typeparam>
 286		private readonly IEqualityComparer<KeyAndValues> equalityComparer;
 287		#endregion
 288
 289		#region allowDuplicateValues (Private)
 290		/// <summary>
 291		/// Whether duplicate values for the same key are allowed.
 292		/// </summary>
 293		/// <typeparam name="TKey">TKey</typeparam>
 294		/// <typeparam name="TValue">TValue</typeparam>
 295		private readonly bool allowDuplicateValues;
 296		#endregion
 297
 298		#region hash (Private)
 299		/// <summary>
 300		/// The hash that holds the keys and values.
 301		/// </summary>
 302		/// <typeparam name="TKey">TKey</typeparam>
 303		/// <typeparam name="TValue">TValue</typeparam>
 304		private Hash<KeyAndValues> hash;
 305		#endregion
 306
 307		#endregion
 308
 309		#region Constructors
 310		/// <summary>
 311		/// Create a new MultiDictionary. The default ordering of keys and values
 312		/// are used. If duplicate values are allowed, multiple copies of the same
 313		/// value can be associated with the same key. For example, the key "foo"
 314		/// could have "a", "a", and "b" associated with it. If duplicate values
 315		/// are not allowed, only one copies of a given value can be associated
 316		/// with the same key, although different keys can have the same value.
 317		/// For example, the key "foo" could have "a" and "b" associated with it,
 318		/// which key "bar" has values "b" and "c" associated with it.
 319		/// </summary>
 320		/// <remarks>The default ordering of keys and values will be used, as
 321		/// defined by TKey and TValue's implementation of IComparable&lt;T&gt;
 322		/// (or IComparable if IComparable&lt;T&gt; is not implemented). If a
 323		/// different ordering should be used, other constructors allow a custom
 324		/// Comparer or IComparer to be passed to changed the ordering.</remarks>
 325		/// <param name="allowDuplicateValues">Can the same value be associated
 326		/// with a key multiple times?</param>
 327		/// <exception cref="InvalidOperationException">TKey or TValue does not
 328		/// implement either IComparable&lt;T&gt; or IComparable.</exception>
 329		public MultiDictionary(bool allowDuplicateValues)
 330			: this(allowDuplicateValues,
 331				EqualityComparer<TKey>.Default,
 332				EqualityComparer<TValue>.Default)
 333		{
 334		}
 335
 336		/// <summary>
 337		/// Create a new MultiDictionary. If duplicate values are allowed,
 338		/// multiple copies of the same value can be associated with the same key.
 339		/// For example, the key "foo" could have "a", "a", and "b" associated
 340		/// with it. If duplicate values are not allowed, only one copies of a
 341		/// given value can be associated with the same key, although different
 342		/// keys can have the same value. For example, the key "foo" could have
 343		/// "a" and "b" associated with it, which key "bar" has values "b" and "c"
 344		/// associated with it.
 345		/// </summary>
 346		/// <param name="allowDuplicateValues">
 347		/// Can the same value be associated with a key multiple times?</param>
 348		/// <param name="keyEqualityComparer">
 349		/// An IEqualityComparer&lt;TKey&gt; instance that will be used to compare
 350		/// keys.</param>
 351		/// <exception cref="InvalidOperationException">TValue does not implement
 352		/// either IComparable&lt;TValue&gt; or IComparable.</exception>
 353		public MultiDictionary(bool allowDuplicateValues,
 354			IEqualityComparer<TKey> keyEqualityComparer)
 355			: this(allowDuplicateValues, keyEqualityComparer,
 356				EqualityComparer<TValue>.Default)
 357		{
 358		}
 359
 360		/// <summary>
 361		/// Create a new MultiDictionary. If duplicate values are allowed,
 362		/// multiple copies of the same value can be associated with the same key.
 363		/// For example, the key "foo" could have "a", "a", and "b" associated
 364		/// with it. If duplicate values are not allowed, only one copies of a
 365		/// given value can be associated with the same key, although different
 366		/// keys can have the same value. For example, the key "foo" could have
 367		/// "a" and "b" associated with it, which key "bar" has values "b" and "c"
 368		/// associated with it.
 369		/// </summary>
 370		/// <param name="allowDuplicateValues">Can the same value be associated
 371		/// with a key multiple times?</param>
 372		/// <param name="keyEqualityComparer">An IEqualityComparer&lt;TKey&gt;
 373		/// instance that will be used to compare keys.</param>
 374		/// <param name="valueEqualityComparer">An IEqualityComparer&lt;TValue&gt;
 375		/// instance that will be used to compare values.</param>
 376		public MultiDictionary(bool allowDuplicateValues,
 377			IEqualityComparer<TKey> keyEqualityComparer,
 378			IEqualityComparer<TValue> valueEqualityComparer)
 379		{
 380			if (keyEqualityComparer == null)
 381			{
 382				throw new ArgumentNullException("keyEqualityComparer");
 383			}
 384			if (valueEqualityComparer == null)
 385			{
 386				throw new ArgumentNullException("valueEqualityComparer");
 387			}
 388
 389			this.allowDuplicateValues = allowDuplicateValues;
 390			this.keyEqualityComparer = keyEqualityComparer;
 391			this.valueEqualityComparer = valueEqualityComparer;
 392			equalityComparer =
 393				new KeyAndValuesEqualityComparer(keyEqualityComparer);
 394			hash = new Hash<KeyAndValues>(equalityComparer);
 395		}
 396
 397		/// <summary>
 398		/// Create a new MultiDictionary. Private constructor, for use by Clone().
 399		/// </summary>
 400		/// <param name="allowDuplicateValues">Allow duplicate values</param>
 401		/// <param name="equalityComparer">Equality comparer</param>
 402		/// <param name="hash">hash</param>
 403		/// <param name="keyEqualityComparer">Key equality comparer</param>
 404		/// <param name="valueEqualityComparer">Value equality comparer</param>
 405		private MultiDictionary(bool allowDuplicateValues,
 406			IEqualityComparer<TKey> keyEqualityComparer,
 407			IEqualityComparer<TValue> valueEqualityComparer,
 408			IEqualityComparer<KeyAndValues> equalityComparer,
 409			Hash<KeyAndValues> hash)
 410		{
 411			if (keyEqualityComparer == null)
 412			{
 413				throw new ArgumentNullException("keyEqualityComparer");
 414			}
 415			if (valueEqualityComparer == null)
 416			{
 417				throw new ArgumentNullException("valueEqualityComparer");
 418			}
 419
 420			this.allowDuplicateValues = allowDuplicateValues;
 421			this.keyEqualityComparer = keyEqualityComparer;
 422			this.valueEqualityComparer = valueEqualityComparer;
 423			this.equalityComparer = equalityComparer;
 424			this.hash = hash;
 425		}
 426		#endregion
 427
 428		#region ICloneable<MultiDictionary<TKey,TValue>> Members
 429		/// <summary>
 430		/// Makes a shallow clone of this dictionary; i.e., if keys or values of
 431		/// the dictionary are reference types, then they are not cloned. If TKey
 432		/// or TValue is a value type, then each element is copied as if by simple
 433		/// assignment.
 434		/// </summary>
 435		/// <remarks>Cloning the dictionary takes time O(N), where N is the number
 436		/// of key-value pairs in the dictionary.</remarks>
 437		/// <returns>The cloned dictionary.</returns>
 438		public MultiDictionary<TKey, TValue> Clone()
 439		{
 440			return new MultiDictionary<TKey, TValue>(
 441				allowDuplicateValues, keyEqualityComparer,
 442				valueEqualityComparer, equalityComparer,
 443				hash.Clone(KeyAndValues.Copy));
 444		}
 445		#endregion
 446
 447		#region Add (Public)
 448		/// <summary>
 449		/// <para>Adds a new value to be associated with a key. If duplicate
 450		/// values are permitted, this method always adds a new key-value pair
 451		/// to the dictionary.</para>
 452		/// <para>If duplicate values are not permitted, and <paramref name="key"/>
 453		/// already has a value equal to <paramref name="value"/> associated with
 454		/// it, then that value is replaced with <paramref name="value"/>,
 455		/// and the number of values associate with <paramref name="key"/> is
 456		/// unchanged.</para>
 457		/// </summary>
 458		/// <param name="key">Key</param>
 459		/// <param name="value">Value</param>
 460		public override sealed void Add(TKey key, TValue value)
 461		{
 462			KeyAndValues keyValues = new KeyAndValues(key);
 463			KeyAndValues existing;
 464
 465			if (hash.Find(keyValues, false, out existing))
 466			{
 467				// There already is an item in the hash table equal to this key.
 468				// Add the new value, taking into account duplicates if needed.
 469				int existingCount = existing.Count;
 470				if (!allowDuplicateValues)
 471				{
 472					int valueHash = GetHashCode(value, valueEqualityComparer);
 473					for (int i = 0; i < existingCount; ++i)
 474					{
 475						if (GetHashCode(existing.Values[i], valueEqualityComparer) ==
 476						    valueHash &&
 477						    valueEqualityComparer.Equals(existing.Values[i], value))
 478						{
 479							// Found an equal existing value. Replace it and we're done.
 480							existing.Values[i] = value;
 481							return;
 482						}
 483					}
 484				}
 485
 486				// Add a new value to an existing key.
 487				if (existingCount == existing.Values.Length)
 488				{
 489					// Grow the array to make room.
 490					TValue[] newValues = new TValue[existingCount * 2];
 491					Array.Copy(existing.Values, newValues, existingCount);
 492					existing.Values = newValues;
 493				}
 494				existing.Values[existingCount] = value;
 495				existing.Count = existingCount + 1;
 496
 497				// Update the hash table.
 498				hash.Find(existing, true, out keyValues);
 499				return;
 500			}
 501			else
 502			{
 503				// No item with this key. Add it.
 504				keyValues.Count = 1;
 505				keyValues.Values = new TValue[1]
 506				{
 507					value
 508				};
 509				hash.Insert(keyValues, true, out existing);
 510				return;
 511			}
 512		}
 513		#endregion
 514
 515		#region Remove (Public)
 516		/// <summary>
 517		/// Removes a given value from the values associated with a key. If the
 518		/// last value is removed from a key, the key is removed also.
 519		/// </summary>
 520		/// <param name="key">A key to remove a value from.</param>
 521		/// <param name="value">The value to remove.</param>
 522		/// <returns>True if <paramref name="value"/> was associated with
 523		/// <paramref name="key"/> (and was therefore removed). False if
 524		/// <paramref name="value"/> was not associated with
 525		/// <paramref name="key"/>.</returns>
 526		public override sealed bool Remove(TKey key, TValue value)
 527		{
 528			KeyAndValues keyValues = new KeyAndValues(key);
 529			KeyAndValues existing;
 530
 531			if (hash.Find(keyValues, false, out existing))
 532			{
 533				// There is an item in the hash table equal to this key.
 534				// Find the value.
 535				int existingCount = existing.Count;
 536				int valueHash = GetHashCode(value, valueEqualityComparer);
 537				int indexFound = -1;
 538				for (int i = 0; i < existingCount; ++i)
 539				{
 540					if (GetHashCode(existing.Values[i], valueEqualityComparer) ==
 541					    valueHash &&
 542					    valueEqualityComparer.Equals(existing.Values[i], value))
 543					{
 544						// Found an equal existing value
 545						indexFound = i;
 546					}
 547				}
 548
 549				if (existingCount == 1)
 550				{
 551					// Removing the last value. Remove the key.
 552					hash.Delete(existing, out keyValues);
 553					return true;
 554				}
 555				else if (indexFound >= 0)
 556				{
 557					// Found a value. Remove it.
 558					if (indexFound < existingCount - 1)
 559					{
 560						Array.Copy(existing.Values, indexFound + 1, existing.Values,
 561							indexFound, existingCount - indexFound - 1);
 562					}
 563					existing.Count = existingCount - 1;
 564
 565					// Update the hash.
 566					hash.Find(existing, true, out keyValues);
 567					return true;
 568				}
 569				else
 570				{
 571					// Value was not found.
 572					return false;
 573				}
 574			}
 575			else
 576			{
 577				return false; // key not found.
 578			}
 579		}
 580
 581		/// <summary>
 582		/// Removes a key and all associated values from the dictionary. If the
 583		/// key is not present in the dictionary, it is unchanged and false is
 584		/// returned.
 585		/// </summary>
 586		/// <param name="key">The key to remove.</param>
 587		/// <returns>True if the key was present and was removed. Returns 
 588		/// false if the key was not present.</returns>
 589		public override sealed bool Remove(TKey key)
 590		{
 591			KeyAndValues dummy;
 592			return hash.Delete(new KeyAndValues(key), out dummy);
 593		}
 594		#endregion
 595
 596		#region Clear (Public)
 597		/// <summary>
 598		/// Removes all keys and values from the dictionary.
 599		/// </summary>
 600		public override sealed void Clear()
 601		{
 602			hash.StopEnumerations(); // Invalidate any enumerations.
 603
 604			// The simplest and fastest way is simply to throw away the old hash
 605			// and create a new one.
 606			hash = new Hash<KeyAndValues>(equalityComparer);
 607		}
 608		#endregion
 609
 610		#region Contains (Public)
 611		/// <summary>
 612		/// Checks to see if <paramref name="value"/> is associated with
 613		/// <paramref name="key"/> in the dictionary.
 614		/// </summary>
 615		/// <param name="key">The key to check.</param>
 616		/// <param name="value">The value to check.</param>
 617		/// <returns>True if <paramref name="value"/> is associated with
 618		/// <paramref name="key"/>.</returns>
 619		public override sealed bool Contains(TKey key, TValue value)
 620		{
 621			KeyAndValues find = new KeyAndValues(key);
 622			KeyAndValues item;
 623			if (hash.Find(find, false, out item))
 624			{
 625				int existingCount = item.Count;
 626				int valueHash = GetHashCode(value, valueEqualityComparer);
 627				for (int i = 0; i < existingCount; ++i)
 628				{
 629					if (GetHashCode(item.Values[i], valueEqualityComparer) ==
 630					    valueHash &&
 631					    valueEqualityComparer.Equals(item.Values[i], value))
 632					{
 633						// Found an equal existing value. 
 634						return true;
 635					}
 636				}
 637			}
 638
 639			return false;
 640		}
 641		#endregion
 642
 643		#region ContainsKey (Public)
 644		/// <summary>
 645		/// Checks to see if the key is present in the dictionary and has
 646		/// at least one value associated with it.
 647		/// </summary>
 648		/// <param name="key">The key to check.</param>
 649		/// <returns>True if <paramref name="key"/> is present and has at least
 650		/// one value associated with it. Returns false otherwise.</returns>
 651		public override sealed bool ContainsKey(TKey key)
 652		{
 653			KeyAndValues find = new KeyAndValues(key);
 654			KeyAndValues temp;
 655			return hash.Find(find, false, out temp);
 656		}
 657		#endregion
 658
 659		#region CloneContents (Public)
 660		/// <summary>
 661		/// Makes a deep clone of this dictionary. A new dictionary is created
 662		/// with a clone of each entry of this dictionary, by calling
 663		/// ICloneable.Clone on each element. If TKey or TValue is a value type,
 664		/// then each element is copied as if by simple assignment.
 665		/// </summary>
 666		/// <remarks><para>If TKey or TValue is a reference type, it must implement
 667		/// ICloneable. Otherwise, an InvalidOperationException is thrown.</para>
 668		/// <para>Cloning the dictionary takes time O(N log N), where N is the
 669		/// number of key-value pairs in the dictionary.</para></remarks>
 670		/// <returns>The cloned dictionary.</returns>
 671		/// <exception cref="InvalidOperationException">TKey or TValue is a
 672		/// reference type that does not implement ICloneable.</exception>
 673		public MultiDictionary<TKey, TValue> CloneContents()
 674		{
 675			bool keyIsValueType, valueIsValueType;
 676
 677			// Make sure that TKey and TValue can be cloned.
 678			if (!IsCloneableType(typeof(TKey), out keyIsValueType))
 679			{
 680				NonCloneableType(typeof(TKey));
 681			}
 682
 683			if (!IsCloneableType(typeof(TValue), out valueIsValueType))
 684			{
 685				NonCloneableType(typeof(TValue));
 686			}
 687
 688			// It's tempting to do a more efficient cloning, utilizing the
 689			// hash.Clone() method. However, we can't know that
 690			// the cloned version of the key has the same hash value.
 691			MultiDictionary<TKey, TValue> newDict =
 692				new MultiDictionary<TKey, TValue>(
 693					allowDuplicateValues, keyEqualityComparer, valueEqualityComparer);
 694
 695			foreach (KeyAndValues item in hash)
 696			{
 697				// Clone the key and values parts. Value types can be cloned
 698				// by just copying them, otherwise, ICloneable is used.
 699				TKey keyClone;
 700				TValue[] valuesClone;
 701
 702				if (keyIsValueType)
 703				{
 704					keyClone = item.Key;
 705				}
 706				else
 707				{
 708					if (item.Key == null)
 709					{
 710						// Really null, because we know TKey isn't a value type.
 711						keyClone = default(TKey);
 712					}
 713					else
 714					{
 715						keyClone = ((ICloneable<TKey>)item.Key).Clone();
 716					}
 717				}
 718
 719				valuesClone = new TValue[item.Count];
 720				if (valueIsValueType)
 721				{
 722					Array.Copy(item.Values, valuesClone, item.Count);
 723				}
 724				else
 725				{
 726					for (int i = 0; i < item.Count; ++i)
 727					{
 728						if (item.Values[i] == null)
 729						{
 730							// Really null, because we know TKey isn't a value type.
 731							valuesClone[i] = default(TValue);
 732						}
 733						else
 734						{
 735							valuesClone[i] = ((ICloneable<TValue>)item.Values[i]).Clone();
 736						}
 737					}
 738				}
 739
 740				newDict.AddMany(keyClone, valuesClone);
 741			}
 742
 743			return newDict;
 744		}
 745		#endregion
 746
 747		#region Methods (Private)
 748
 749		#region EqualValues
 750		/// <summary>
 751		/// Determine if two values are equal.
 752		/// </summary>
 753		/// <param name="value1">First value to compare.</param>
 754		/// <param name="value2">Second value to compare.</param>
 755		/// <returns>True if the values are equal.</returns>
 756		protected override sealed bool EqualValues(TValue value1, TValue value2)
 757		{
 758			return valueEqualityComparer.Equals(value1, value2);
 759		}
 760		#endregion
 761
 762		#region EnumerateKeys
 763		/// <summary>
 764		/// Enumerate all the keys in the dictionary. 
 765		/// </summary>
 766		/// <returns>
 767		/// An IEnumerator&lt;TKey&gt; that enumerates all of the keys in the
 768		/// dictionary that have at least one value associated with them.
 769		/// </returns>
 770		protected override sealed IEnumerator<TKey> EnumerateKeys()
 771		{
 772			foreach (KeyAndValues item in hash)
 773			{
 774				yield return item.Key;
 775			}
 776		}
 777		#endregion
 778
 779		#region EnumerateValues
 780		/// <summary>
 781		///  Enumerate the values in the a KeyAndValues structure. Can't return
 782		/// the array directly because:
 783		///   a) The array might be larger than the count.
 784		///   b) We can't allow clients to down-cast to the array and modify it.
 785		///   c) We have to abort enumeration if the hash changes.
 786		/// </summary>
 787		/// <param name="keyAndValues">Item with the values to enumerate..</param>
 788		/// <returns>An enumerable that enumerates the items in the KeyAndValues
 789		/// structure.</returns>
 790		private IEnumerator<TValue> EnumerateValues(KeyAndValues keyAndValues)
 791		{
 792			int count = keyAndValues.Count;
 793			int stamp = hash.GetEnumerationStamp();
 794
 795			for (int i = 0; i < count; ++i)
 796			{
 797				yield return keyAndValues.Values[i];
 798				hash.CheckEnumerationStamp(stamp);
 799			}
 800		}
 801		#endregion
 802
 803		#region TryEnumerateValuesForKey
 804		/// <summary>
 805		/// Determines if this dictionary contains a key equal to
 806		/// <paramref name="key"/>. If so, all the values associated with that key
 807		/// are returned through the values parameter.
 808		/// </summary>
 809		/// <param name="key">The key to search for.</param>
 810		/// <param name="values">Returns all values associated with key, if true
 811		/// was returned.</param>
 812		/// <returns>True if the dictionary contains key. False if the dictionary
 813		/// does not contain key.</returns>
 814		protected override sealed bool TryEnumerateValuesForKey(
 815			TKey key, out IEnumerator<TValue> values)
 816		{
 817			KeyAndValues find = new KeyAndValues(key);
 818			KeyAndValues item;
 819			if (hash.Find(find, false, out item))
 820			{
 821				values = EnumerateValues(item);
 822				return true;
 823			}
 824			else
 825			{
 826				values = null;
 827				return false;
 828			}
 829		}
 830		#endregion
 831
 832		#region CountValues
 833		/// <summary>
 834		/// Gets the number of values associated with a given key.
 835		/// </summary>
 836		/// <param name="key">The key to count values of.</param>
 837		/// <returns>The number of values associated with <paramref name="key"/>.
 838		/// If <paramref name="key"/> is not present in the dictionary, zero is
 839		/// returned.</returns>
 840		protected override sealed int CountValues(TKey key)
 841		{
 842			KeyAndValues find = new KeyAndValues(key);
 843			KeyAndValues item;
 844			if (hash.Find(find, false, out item))
 845			{
 846				return item.Count;
 847			}
 848			else
 849			{
 850				return 0;
 851			}
 852		}
 853		#endregion
 854
 855		#region NonCloneableType
 856		/// <summary>
 857		/// Throw an InvalidOperationException indicating that this type is not
 858		/// cloneable.
 859		/// </summary>
 860		/// <param name="t">Type to test.</param>
 861		private void NonCloneableType(Type t)
 862		{
 863			throw new InvalidOperationException(
 864				"Type " + t.FullName + " does not implement ICloneable.");
 865		}
 866		#endregion
 867
 868		#endregion
 869	}
 870
 871	/// <summary>
 872	/// Multi dictionary tests, needs to be an extra class because
 873	/// MultiDictionary is generic.
 874	/// </summary>
 875	internal class MultiDictionaryTests
 876	{
 877		#region Helpers
 878
 879		#region CheckMultiDictionaryContents
 880		/// <summary>
 881		/// Check the contents of a Multi-Dictionary non-destructively.
 882		/// Keys and Values must be in order.
 883		/// </summary>
 884		/// <param name="dict">Dictionary</param>
 885		/// <param name="keys">Keys</param>
 886		/// <param name="values">Values</param>
 887		/// <param name="nonKey">Non key</param>
 888		/// <param name="nonValue">Non value</param>
 889		internal static void CheckMultiDictionaryContents<TKey, TValue>(
 890			MultiDictionary<TKey, TValue> dict, TKey[] keys, TValue[][] values,
 891			TKey nonKey, TValue nonValue)
 892		{
 893			//Optimize performance of this test helper method, it is still to slow
 894			int iKey, iValue;
 895			ICollection<TValue> getValues;
 896
 897			// Check Count.
 898			Assert.Equal(keys.Length, dict.Count);
 899
 900			// Check indexer, ContainsKey, Contains, TryGetValue for each key.
 901			for (iKey = 0; iKey < keys.Length; ++iKey)
 902			{
 903				Assert.True(dict.ContainsKey(keys[iKey]));
 904				Assert.True(dict.Contains(
 905					new KeyValuePair<TKey, ICollection<TValue>>(
 906					keys[iKey], values[iKey])));
 907
 908				bool b = ((IDictionary<TKey, ICollection<TValue>>)dict).TryGetValue(
 909					keys[iKey], out getValues);
 910				Assert.True(b);
 911				iValue = 0;
 912				CollectionBaseTests.CheckEnumerableElementsAnyOrder(
 913					getValues, values[iKey]);
 914
 915				iValue = 0;
 916				foreach (TValue val in values[iKey])
 917				{
 918					Assert.True(dict.Contains(keys[iKey], val));
 919					++iValue;
 920				} // foreach
 921				Assert.True(iValue == values[iKey].Length);
 922
 923				iValue = 0;
 924				getValues = dict[keys[iKey]];
 925				CollectionBaseTests.CheckEnumerableElementsAnyOrder(
 926					getValues, values[iKey]);
 927			} // for (iKey)
 928
 929			// Check Keys collection.
 930			CollectionBaseTests.CompareCollections(
 931				dict.Keys, keys, false);
 932
 933			// Check Values collection
 934			int a = 0;
 935			TValue[] vals = new TValue[dict.Values.Count];
 936			for (iKey = 0; iKey < keys.Length; ++iKey)
 937			{
 938				for (iValue = 0; iValue < values[iKey].Length; ++iValue)
 939				{
 940					vals[a++] = values[iKey][iValue];
 941				} // for (iValue)
 942			} // for (iKey)
 943			Assert.Equal(dict.Values.Count, a);
 944			CollectionBaseTests.CompareCollections(
 945				dict.Values, vals, false);
 946
 947			// Check KeyValuePairs collection.
 948			a = 0;
 949			KeyValuePair<TKey, TValue>[] pairs =
 950				new KeyValuePair<TKey, TValue>[dict.Values.Count];
 951			for (iKey = 0; iKey < keys.Length; ++iKey)
 952			{
 953				for (iValue = 0; iValue < values[iKey].Length; ++iValue)
 954				{
 955					pairs[a++] = new KeyValuePair<TKey, TValue>(
 956						keys[iKey], values[iKey][iValue]);
 957				} // for (iValue)
 958			} // for (iKey)
 959			CollectionBaseTests.CompareCollections(
 960				dict.KeyValuePairs, pairs, false);
 961
 962			// Tests Contains, ContainsKey, TryGetValue for wrong values.
 963			Assert.False(dict.ContainsKey(nonKey));
 964			Assert.False(((IDictionary<TKey, ICollection<TValue>>)dict).
 965				TryGetValue(nonKey, out getValues));
 966			for (iKey = 0; iKey < keys.Length; ++iKey)
 967			{
 968				Assert.False(dict.Contains(keys[iKey], nonValue));
 969				Assert.False(dict.Contains(
 970					new KeyValuePair<TKey, ICollection<TValue>>(
 971					keys[iKey], new TValue[1] { nonValue })));
 972			}
 973		}
 974		#endregion
 975
 976		#region FirstLetterComparer
 977		/// <summary>
 978		/// First letter comparer
 979		/// </summary>
 980		private class FirstLetterComparer : IEqualityComparer<string>
 981		{
 982			#region IEqualityComparer<string> Members
 983			/// <summary>
 984			/// Equals
 985			/// </summary>
 986			/// <param name="x">X value</param>
 987			/// <param name="y">Y value</param>
 988			/// <returns>True if both x and y match</returns>
 989			public bool Equals(string x, string y)
 990			{
 991				if (x == null)
 992				{
 993					return y == null;
 994				}
 995				else if (x.Length == 0)
 996				{
 997					return (y != null && y.Length == 0);
 998				}
 999				else
1000				{
1001					if (y == null ||
1002					    y.Length == 0)
1003					{
1004						return false;
1005					}
1006					else
1007					{
1008						return x[0] == y[0];
1009					}
1010				}
1011			}
1012
1013			/// <summary>
1014			/// Get hash code
1015			/// </summary>
1016			/// <param name="obj">Object</param>
1017			/// <returns>Int</returns>
1018			public int GetHashCode(string obj)
1019			{
1020				if (obj == null)
1021				{
1022					return 0x12383;
1023				}
1024				else if (obj.Length == 0)
1025				{
1026					return 17;
1027				}
1028				else
1029				{
1030					return obj[0].GetHashCode();
1031				}
1032			}
1033			#endregion
1034		}
1035		#endregion
1036
1037		#region CompareClones
1038		/// <summary>
1039		/// Compare clones
1040		/// </summary>
1041		/// <param name="d1">D 1</param>
1042		/// <param name="d2">D 2</param>
1043		private void CompareClones<K, V>(
1044			MultiDictionary<K, V> d1, MultiDictionary<K, V> d2)
1045		{
1046			IEnumerable<KeyValuePair<K, V>> e1 = d1.KeyValuePairs;
1047			IEnumerable<KeyValuePair<K, V>> e2 = d2.KeyValuePairs;
1048			KeyValuePair<K, V>[]
1049				pairs1 = ArrayHelper.ToArray(e1),
1050				pairs2 = ArrayHelper.ToArray(e2);
1051			bool[] found = new bool[pairs2.Length];
1052
1053			// Check that the arrays are equal, but not reference equals
1054			// (e.g., have been cloned).
1055			Assert.True(pairs1.Length == pairs2.Length);
1056			foreach (KeyValuePair<K, V> p1 in pairs1)
1057			{
1058				bool f = false;
1059				for (int i = 0; i < pairs2.Length; ++i)
1060				{
1061					if (!found[i] &&
1062					    Equals(p1.Key, pairs2[i].Key) &&
1063					    Equals(p1.Value, pairs2[i].Value))
1064					{
1065						found[i] = true;
1066						f = true;
1067						Assert.True(p1.Key == null ||
1068						            !ReferenceEquals(p1.Key, pairs2[i].Key));
1069						Assert.True(p1.Value == null ||
1070						            !ReferenceEquals(p1.Value, pairs2[i].Value));
1071						break;
1072					}
1073				}
1074				Assert.True(f);
1075			}
1076		}
1077		#endregion
1078
1079		#endregion
1080
1081		#region Clone (LongRunning)
1082		/// <summary>
1083		/// Clone. Note: Too slow for a dynamic unit test.
1084		/// </summary>
1085		[Test, Category("LongRunning")]
1086		public static void Clone()
1087		{
1088			IEqualityComparer<string> firstLetterComparer =
1089				new FirstLetterComparer();
1090
1091			MultiDictionary<string, string> dict1 =
1092				new MultiDictionary<string, string>(false,
1093					StringComparer.InvariantCultureIgnoreCase,
1094					firstLetterComparer);
1095
1096			dict1.Add("qubert", "dinosaur");
1097			dict1.Add("Hello", "AAA");
1098			dict1.Add("Hi", "aaa");
1099			dict1.Add("Qubert", "hello");
1100			dict1.Add("queztel", "hello");
1101			dict1.Add("Alpha", "omega");
1102			dict1.Add("alpha", "oz");
1103			dict1.Add("qubert", "hippy");
1104
1105			MultiDictionary<string, string> dict2 = dict1.Clone();
1106
1107			Assert.True(dict1 != dict2);
1108
1109			dict2.Add("qubert", "hoover");
1110			dict2.Remove("queztel");
1111			dict2.Add("hello", "banana");
1112
1113			CollectionBaseTests.CheckEnumerableElementsAnyOrder(
1114				dict1.KeyValuePairs, new[]
1115				{
1116					new KeyValuePair<string, string>("Alpha", "oz"),
1117					new KeyValuePair<string, string>("Hello", "AAA"),
1118					new KeyValuePair<string, string>("Hi", "aaa"),
1119					new KeyValuePair<string, string>("qubert", "hippy"),
1120					new KeyValuePair<string, string>("qubert", "dinosaur"),
1121					new KeyValuePair<string, string>("queztel", "hello")
1122				});
1123
1124			CollectionBaseTests.CheckEnumerableElementsAnyOrder(
1125				dict2.KeyValuePairs, new[]
1126				{
1127					new KeyValuePair<string, string>("Alpha", "oz"),
1128					new KeyValuePair<string, string>("Hello", "banana"),
1129					new KeyValuePair<string, string>("Hello", "AAA"),
1130					new KeyValuePair<string, string>("Hi", "aaa"),
1131					new KeyValuePair<string, string>("qubert", "hoover"),
1132					new KeyValuePair<string, string>("qubert", "dinosaur")
1133				});
1134
1135			dict2 = dict1.Clone();
1136
1137			Assert.True(dict1 != dict2);
1138
1139			dict2.Add("qubert", "hoover");
1140			dict2.Remove("queztel");
1141			dict2.Add("hello", "banana");
1142
1143			CollectionBaseTests.CheckEnumerableElementsAnyOrder(
1144				dict2.KeyValuePairs, new[]
1145				{
1146					new KeyValuePair<string, string>("Alpha", "oz"),
1147					new KeyValuePair<string, string>("Hello", "banana"),
1148					new KeyValuePair<string, string>("Hello", "AAA"),
1149					new KeyValuePair<string, string>("Hi", "aaa"),
1150					new KeyValuePair<string, string>("qubert", "hoover"),
1151					new KeyValuePair<string, string>("qubert", "dinosaur")
1152				});
1153
1154			MultiDictionary<string, int> dict4 =
1155				new MultiDictionary<string, int>(true);
1156			MultiDictionary<string, int> dict5;
1157			dict5 = dict4.Clone();
1158			Assert.False(dict4 == dict5);
1159			Assert.True(dict4.Count == 0 && dict5.Count == 0);
1160			dict4.Add("hello", 1);
1161			Assert.True(dict4.Count == 1 && dict5.Count == 0);
1162			dict5.Add("hi", 7);
1163			dict4.Clear();
1164			Assert.True(dict4.Count == 0 && dict5.Count == 1);
1165		}
1166		#endregion
1167
1168		#region TestAddElements (LongRunning)
1169		/// <summary>
1170		/// Test add elements. Note: Too slow for a dynamic unit test.
1171		/// </summary>
1172		[Test, Category("LongRunning")]
1173		public static void TestAddElements()
1174		{
1175			// Test without duplicate values.
1176			MultiDictionary<string, double> dict1 =
1177				new MultiDictionary<string, double>(false);
1178
1179			dict1.Add("foo", 3.5);
1180			dict1.Add("foo", -1.2);
1181			dict1.Add(null, 11.1);
1182			dict1.Add("foo", 8.8);
1183			dict1.Add(null, 11.1);
1184			dict1.Add("bar", 9.8);
1185			dict1.Add("foo", 8.8);
1186			dict1.Add("gib", 7.1);
1187			dict1.Add("S", -9);
1188			dict1.Add(null, 5.5);
1189
1190			Assert.Equal("3.5, -1.2, 8.8",
1191				dict1["foo"].Write());
1192			Assert.Equal("7.1",
1193				dict1["gib"].Write());
1194			Assert.Equal("11.1, 5.5",
1195				dict1[null].Write());
1196
1197			// Test with duplicate values.
1198			dict1 = new MultiDictionary<string, double>(true);
1199
1200			dict1.Add("foo", 3.5);
1201			dict1.Add("foo", -1.2);
1202			dict1.Add(null, 11.1);
1203			dict1.Add("foo", 8.8);
1204			dict1.Add(null, 11.1);
1205			dict1.Add("bar", 9.8);
1206			dict1.Add("foo", 8.8);
1207			dict1.Add("gib", 7.1);
1208			dict1.Add("S", -9);
1209			dict1.Add(null, 5.5);
1210
1211			Assert.Equal("3.5, -1.2, 8.8, 8.8",
1212				dict1["foo"].Write());
1213			Assert.Equal("7.1",
1214				dict1["gib"].Write());
1215			Assert.Equal("11.1, 11.1, 5.5",
1216				dict1[null].Write());
1217		}
1218		#endregion
1219
1220		#region AddManyOne (LongRunning)
1221		/// <summary>
1222		/// Add many One. Note: Too slow for a dynamic unit test.
1223		/// </summary>
1224		[Test, Category("LongRunning")]
1225		public static void AddManyOne()
1226		{
1227			// Test without duplicate values.
1228			MultiDictionary<string, double> dict1 =
1229				new MultiDictionary<string, double>(
1230					false, StringComparer.InvariantCultureIgnoreCase);
1231
1232			dict1.AddMany("foo", new[]
1233			{
1234				9.8, 1.2, -9, 9.8, -9, 4
1235			});
1236			dict1.AddMany("hi", new double[0]);
1237			dict1.AddMany("FOO", new double[]
1238			{
1239				8, -9
1240			});
1241
1242			Assert.Equal(1, dict1.Count);
1243			Assert.True(dict1.ContainsKey("foo"));
1244			Assert.False(dict1.ContainsKey("hi"));
1245			CollectionBaseTests.CheckEnumerableElementsAnyOrder(
1246				dict1.Keys, new[]
1247				{
1248					"foo"
1249				});
1250			CollectionBaseTests.CheckEnumerableElementsAnyOrder(
1251				dict1["fOo"], new[]
1252				{
1253					-9, 1.2, 4, 8, 9.8
1254				});
1255			CollectionBaseTests.CheckEnumerableElementsAnyOrder(
1256				dict1.KeyValuePairs, new[]
1257				{
1258					new KeyValuePair<string, double>("foo", -9),
1259					new KeyValuePair<string, double>("foo", 1.2),
1260					new KeyValuePair<string, double>("foo", 4),
1261					new KeyValuePair<string, double>("foo", 8),
1262					new KeyValuePair<string, double>("foo", 9.8)
1263				});
1264
1265			// Test with duplicate values
1266			dict1 = new MultiDictionary<string, double>(
1267				true, StringComparer.InvariantCultureIgnoreCase);
1268
1269			dict1.AddMany("foo", new[]
1270			{
1271				9.8, 1.2, -9, 9.8, -9, 4
1272			});
1273			dict1.AddMany("hi", new double[0]);
1274			dict1.AddMany("a", new double[]
1275			{
1276				2, 1, 2
1277			});
1278			dict1.AddMany("FOO", new double[]
1279			{
1280				8, -9
1281			});
1282
1283			Assert.Equal(2, dict1.Count);
1284			Assert.True(dict1.ContainsKey("foo"));
1285			Assert.False(dict1.ContainsKey("hi"));
1286			CollectionBaseTests.CheckEnumerableElementsAnyOrder(
1287				dict1.Keys, new[]
1288				{
1289					"a", "foo"
1290				});
1291			CollectionBaseTests.CheckEnumerableElementsAnyOrder(
1292				dict1["fOo"], new[]
1293				{
1294					-9, -9, -9, 1.2, 4, 8, 9.8, 9.8
1295				});
1296			CollectionBaseTests.CheckEnumerableElementsAnyOrder(
1297				dict1.KeyValuePairs, new[]
1298				{
1299					new KeyValuePair<string, double>("a", 1),
1300					new KeyValuePair<string, double>("a", 2),
1301					new KeyValuePair<string, double>("a", 2),
1302					new KeyValuePair<string, double>("foo", -9),
1303					new KeyValuePair<string, double>("foo", -9),
1304					new KeyValuePair<string, double>("foo", -9),
1305					new KeyValuePair<string, double>("foo", 1.2),
1306					new KeyValuePair<string, double>("foo", 4),
1307					new KeyValuePair<string, double>("foo", 8),
1308					new KeyValuePair<string, double>("foo", 9.8),
1309					new KeyValuePair<string, double>("foo", 9.8)
1310				});
1311		}
1312		#endregion
1313
1314		#region Replace (LongRunning)
1315		/// <summary>
1316		/// Replace. Note: Too slow for a dynamic unit test.
1317		/// </summary>
1318		[Test, Category("LongRunning")]
1319		public static void Replace()
1320		{
1321			MultiDictionary<string, int> dict1 =
1322				new MultiDictionary<string, int>(true);
1323
1324			dict1.Add("foo", 4);
1325			dict1.Add("bar", 7);
1326			dict1.Add("foo", 6);
1327			dict1.Add("z", 3);
1328			dict1.Add("bar", 8);
1329			dict1.Add("z", 3);
1330			dict1.Add("foo", 1);
1331
1332			dict1.Replace("foo", 13);
1333			dict1.Replace("z", 19);
1334			dict1.Replace("hello", 193);
1335			dict1.Replace("foo", 123);
1336			dict1.Add("foo", 123);
1337
1338			CheckMultiDictionaryContents(dict1,
1339				new[]
1340				{
1341					"bar", "foo", "hello", "z"
1342				},
1343				new[]
1344				{
1345					new[]
1346					{
1347						7, 8
1348					},
1349					new[]
1350					{
1351						123, 123
1352					},
1353					new[]
1354					{
1355						193
1356					},
1357					new[]
1358					{
1359						19
1360					}
1361				},
1362				"sailor", 19921);
1363		}
1364		#endregion
1365
1366		#region ReplaceMany (LongRunning)
1367		/// <summary>
1368		/// Replace many. Note: Too slow for a dynamic unit test.
1369		/// </summary>
1370		[Test, Category("LongRunning")]
1371		public static void ReplaceMany()
1372		{
1373			MultiDictionary<string, int> dict1 =
1374				new MultiDictionary<string, int>(false);
1375
1376			dict1.Add("foo", 4);
1377			dict1.Add("bar", 7);
1378			dict1.Add("foo", 6);
1379			dict1.Add("z", 3);
1380			dict1.Add("bar", 8);
1381			dict1.Add("z", 3);
1382			dict1.Add("foo", 1);
1383			dict1.Add("bill", 9);
1384
1385			dict1.ReplaceMany("bill", new int[0]);
1386			dict1.ReplaceMany("foo", new[]
1387			{
1388				13, 4
1389			});
1390			dict1.ReplaceMany("z", new[]
1391			{
1392				19
1393			});
1394			dict1.ReplaceMany("hello", new[]
1395			{
1396				193, -11, 193
1397			});
1398			dict1.ReplaceMany("goodbye", new int[0]);
1399			dict1.ReplaceMany("foo", new[]
1400			{
1401				123, 0, 4
1402			});
1403			dict1.Add("foo", 29);
1404
1405			CheckMultiDictionaryContents(dict1,
1406				new[]
1407				{
1408					"bar", "foo", "hello", "z"
1409				},
1410				new[]
1411				{
1412					new[]
1413					{
1414						7, 8
1415					},
1416					new[]
1417					{
1418						0, 4, 29, 123
1419					},
1420					new[]
1421					{
1422						-11, 193
1423					},
1424					new[]
1425					{
1426						19
1427					}
1428				},
1429				"sailor", 19921);
1430		}
1431		#endregion
1432
1433		#region RemoveKey (LongRunning)
1434		/// <summary>
1435		/// Remove key. Note: Too slow for a dynamic unit test.
1436		/// </summary>
1437		[Test, Category("LongRunning")]
1438		public static void RemoveKey()
1439		{
1440			MultiDictionary<string, int> dict1 =
1441				new MultiDictionary<string, int>(true);
1442
1443			dict1.Add("foo", 4);
1444			dict1.Add("bar", 7);
1445			dict1.Add("foo", 6);
1446			dict1.Add("z", 3);
1447			dict1.Add("bar", 8);
1448			dict1.Add("z", 10);
1449			dict1.Add("z", 3);
1450			dict1.Add("foo", 4);
1451			dict1.Add("bill", 9);
1452
1453			Assert.True(dict1.ContainsKey("bill"));
1454			Assert.True(dict1.ContainsKey("foo"));
1455			Assert.True(dict1.ContainsKey("z"));
1456
1457			Assert.True(dict1.Remove("bill"));
1458			Assert.False(dict1.Remove("bill"));
1459			Assert.False(dict1.Remove("smell"));
1460			Assert.True(dict1.Remove("foo"));
1461
1462			CheckMultiDictionaryContents(dict1,
1463				new[]
1464				{
1465					"bar", "z"
1466				},
1467				new[]
1468				{
1469					new[]
1470					{
1471						7, 8
1472					}, new[]
1473					{
1474						3, 3, 10
1475					}
1476				},
1477				"sailor", 19921);
1478		}
1479		#endregion
1480
1481		#region RemoveManyKeys (LongRunning)
1482		/// <summary>
1483		/// Remove many keys. Note: Too slow for a dynamic unit test.
1484		/// </summary>
1485		[Test, Category("LongRunning")]
1486		public static void RemoveManyKeys()
1487		{
1488			MultiDictionary<string, int> dict1 =
1489				new MultiDictionary<string, int>(true);
1490
1491			dict1.Add("foo", 4);
1492			dict1.Add("bar", 7);
1493			dict1.Add("foo", 6);
1494			dict1.Add("z", 3);
1495			dict1.Add("bar", 8);
1496			dict1.Add("z", 10);
1497			dict1.Add("z", 3);
1498			dict1.Add("foo", 4);
1499			dict1.Add("bill", 9);
1500
1501			Assert.True(dict1.ContainsKey("bill"));
1502			Assert.True(dict1.ContainsKey("foo"));
1503			Assert.True(dict1.ContainsKey("z"));
1504
1505			Assert.Equal(2, dict1.RemoveMany(
1506				new[]
1507				{
1508					"bill", "smell", "foo", "bill"
1509				}));
1510
1511			CheckMultiDictionaryContents(dict1,
1512				new[]
1513				{
1514					"bar", "z"
1515				},
1516				new[]
1517				{
1518					new[]
1519					{
1520						7, 8
1521					}, new[]
1522					{
1523						3, 3, 10
1524					}
1525				},
1526				"sailor", 19921);
1527		}
1528		#endregion
1529
1530		#region Remove (LongRunning)
1531		/// <summary>
1532		/// Remove. Note: Too slow for a dynamic unit test.
1533		/// </summary>
1534		[Test, Category("LongRunning")]
1535		public static void Remove()
1536		{
1537			MultiDictionary<string, int> dict1 =
1538				new MultiDictionary<string, int>(true);
1539
1540			dict1.Add("foo", 4);
1541			dict1.Add("bar", 7);
1542			dict1.Add("foo", 6);
1543			dict1.Add("z", 3);
1544			dict1.Add("bar", 8);
1545			dict1.Add("z", 10);
1546			dict1.Add("z", 3);
1547			dict1.Add("foo", 4);
1548			dict1.Add("bill", 9);
1549			dict1.Add("foo", 4);
1550
1551			Assert.True(dict1.Remove("foo", 4));
1552			Assert.True(dict1.Remove("foo", 4));
1553			Assert.True(dict1.Remove("z", 10));
1554			Assert.False(dict1.Remove("z", 10));
1555			Assert.False(dict1.Remove("foo", 11));
1556			Assert.False(dict1.Remove(null, 0));
1557			Assert.True(dict1.Remove("bill", 9));
1558
1559			CheckMultiDictionaryContents(dict1,
1560				new[]
1561				{
1562					"bar", "foo", "z"
1563				},
1564				new[]
1565				{
1566					new[]
1567					{
1568						7, 8
1569					},
1570					new[]
1571					{
1572						4, 6
1573					},
1574					new[]
1575					{
1576						3, 3
1577					}
1578				},
1579				"sailor", 19921);
1580		}
1581		#endregion
1582
1583		#region RemoveManyOne (LongRunning)
1584		/// <summary>
1585		/// Remove many One. Note: Too slow for a dynamic unit test.
1586		/// </summary>
1587		[Test, Category("LongRunning")]
1588		public static void RemoveManyOne()
1589		{
1590			MultiDictionary<string, int> dict1 =
1591				new MultiDictionary<string, int>(true);
1592
1593			dict1.Add("bill", 7);
1594			dict1.Add("foo", 4);
1595			dict1.Add("bar", 7);
1596			dict1.Add("foo", 6);
1597			dict1.Add("z", 3);
1598			dict1.Add("bar", 8);
1599			dict1.Add("z", 10);
1600			dict1.Add("z", 3);
1601			dict1.Add("foo", 4);
1602			dict1.Add("bill", 9);
1603			dict1.Add("foo", 4);
1604
1605			Assert.Equal(2, dict1.RemoveMany("foo", new[]
1606			{
1607				4, 11, 4
1608			}));
1609			Assert.Equal(1, dict1.RemoveMany("z", new[]
1610			{
1611				9, 2, 10
1612			}));
1613			Assert.Equal(0, dict1.RemoveMany("z", new[]
1614			{
1615				10, 16, 144, 10
1616			}));
1617			Assert.Equal(0, dict1.RemoveMany("foo", new int[0]));
1618			Assert.Equal(0, dict1.RemoveMany(null, new int[2]
1619			{
1620				1, 2
1621			}));
1622			Assert.Equal(2, dict1.RemoveMany("bill", new[]
1623			{
1624				9, 7
1625			}));
1626
1627			CheckMultiDictionaryContents(dict1,
1628				new[]
1629				{
1630					"bar", "foo", "z"
1631				},
1632				new[]
1633				{
1634					new[]
1635					{
1636						7, 8
1637					},
1638					new[]
1639					{
1640						4, 6
1641					},
1642					new[]
1643					{
1644						3, 3
1645					}
1646				},
1647				"sailor", 19921);
1648		}
1649		#endregion
1650
1651		#region Clear (LongRunning)
1652		/// <summary>
1653		/// Clear. Note: Too slow for a dynamic unit test.
1654		/// </summary>
1655		[Test, Category("LongRunning")]
1656		public static void Clear()
1657		{
1658			MultiDictionary<string, int> dict1 =
1659				new MultiDictionary<string, int>(true);
1660
1661			dict1.Add("foo", 4);
1662			dict1.Add("bill", 7);
1663			dict1.Add("foo", 4);
1664			dict1.Add("bar", 7);
1665			dict1.Add("foo", 6);
1666			dict1.Add("z", 3);
1667			dict1.Add("bar", 8);
1668			dict1.Add("z", 10);
1669			dict1.Add(null, 3);
1670			dict1.Add("foo", 4);
1671			dict1.Add("bill", 9);
1672			dict1.Add("foo", 4);
1673
1674			dict1.Clear();
1675
1676			Assert.Equal(0, dict1.Count);
1677			Assert.False(dict1.ContainsKey("foo"));
1678			Assert.False(dict1.ContainsKey("z"));
1679			Assert.False(dict1.ContainsKey(null));
1680			Assert.Equal(0, dict1.Keys.Count);
1681			Assert.Equal(0, dict1.Values.Count);
1682			Assert.Equal(0, dict1.KeyValuePairs.Count);
1683
1684			CheckMultiDictionaryContents(dict1, new string[0],
1685				new int[0][], "foo", 4);
1686		}
1687		#endregion
1688
1689		#region Count (LongRunning)
1690		/// <summary>
1691		/// Count. Note: Too slow for a dynamic unit test.
1692		/// </summary>
1693		[Test, Category("LongRunning")]
1694		public static void Count()
1695		{
1696			MultiDictionary<string, int> dict1 =
1697				new MultiDictionary<string, int>(true);
1698
1699			dict1.Add("foo", 4);
1700			dict1.Add(null, 7);
1701			dict1.Add("bar", 11);
1702			dict1.Add("foo", 7);
1703			dict1.Add(null, 7);
1704			dict1.Add("hello", 11);
1705			dict1.Add("foo", 4);
1706			Assert.Equal(4, dict1.Count);
1707
1708			MultiDictionary<string, int> dict2 =
1709				new MultiDictionary<string, int>(false);
1710
1711			dict2.Add("foo", 4);
1712			dict2.Add(null, 7);
1713			dict2.Add("bar", 11);
1714			dict2.Add("foo", 7);
1715			dict2.Add(null, 7);
1716			dict2.Add("hello", 11);
1717			dict2.Add("foo", 4);
1718			Assert.Equal(4, dict2.Count);
1719
1720			dict2.Remove("foo");
1721			Assert.Equal(3, dict2.Count);
1722
1723			dict2.Clear();
1724			Assert.Equal(0, dict2.Count);
1725		}
1726		#endregion
1727
1728		#region ContainsKey (LongRunning)
1729		/// <summary>
1730		/// Contains key. Note: Too slow for a dynamic unit test.
1731		/// </summary>
1732		[Test, Category("LongRunning")]
1733		public static void ContainsKey()
1734		{
1735			MultiDictionary<string, int> dict1 =
1736				new MultiDictionary<string, int>(true);
1737
1738			dict1.Add("foo", 4);
1739			dict1.Add(null, 7);
1740			dict1.Add("bar", 11);
1741			dict1.Add("foo", 7);
1742			dict1.Add(null, 7);
1743			dict1.Add("hello", 11);
1744			dict1.Add("foo", 4);
1745
1746			Assert.True(dict1.ContainsKey(null));
1747			Assert.True(dict1.ContainsKey("foo"));
1748			Assert.True(dict1

Large files files are truncated, but you can click here to view the full file