diff options
author | Marek Safar <marek.safar@gmail.com> | 2010-04-28 14:57:10 +0400 |
---|---|---|
committer | Marek Safar <marek.safar@gmail.com> | 2010-04-28 14:57:10 +0400 |
commit | f219d8a55f9daf9a152c23a137f22e686f31520d (patch) | |
tree | 1fb613a3afbcd8e02d9235ae1132304acba018c6 /mcs | |
parent | 7cd89dc1a6b0c849a9043a136e69b0fd61ae54f8 (diff) |
set eolstyle correctly
svn path=/trunk/mcs/; revision=156306
Diffstat (limited to 'mcs')
25 files changed, 22359 insertions, 22359 deletions
diff --git a/mcs/class/Mono.C5/C5/AssemblyInfo.cs b/mcs/class/Mono.C5/C5/AssemblyInfo.cs index 187181f6244..7ffe0d36eb2 100644 --- a/mcs/class/Mono.C5/C5/AssemblyInfo.cs +++ b/mcs/class/Mono.C5/C5/AssemblyInfo.cs @@ -1,81 +1,81 @@ -/*
- Copyright (c) 2003-2007 Niels Kokholm and Peter Sestoft
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System;
-
-//
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-//
-[assembly: AssemblyTitle("C5: Copenhagen Comprehensive Collection Classes for CLI")]
-[assembly: AssemblyDescription("This is a build of release 1.1.0")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("")]
-[assembly: AssemblyCopyright("(c) 2003-2008 Niels Kokholm and Peter Sestoft")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-//
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Revision and Build Numbers
-// by using the '*' as shown below:
-
-[assembly: AssemblyVersion("1.1.0")]
-
-//
-// In order to sign your assembly you must specify a key to use. Refer to the
-// Microsoft .NET Framework documentation for more information on assembly signing.
-//
-// Use the attributes below to control which key is used for signing.
-//
-// Notes:
-// (*) If no key is specified, the assembly is not signed.
-// (*) KeyName refers to a key that has been installed in the Crypto Service
-// Provider (CSP) on your machine. KeyFile refers to a file which contains
-// a key.
-// (*) If the KeyFile and the KeyName values are both specified, the
-// following processing occurs:
-// (1) If the KeyName can be found in the CSP, that key is used.
-// (2) If the KeyName does not exist and the KeyFile does exist, the key
-// in the KeyFile is installed into the CSP and used.
-// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
-// When specifying the KeyFile, the location of the KeyFile should be
-// relative to the project output directory which is
-// %Project Directory%\obj\<configuration>. For example, if your KeyFile is
-// located in the project directory, you would specify the AssemblyKeyFile
-// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
-// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
-// documentation for more information on this.
-//
-[assembly: AssemblyDelaySign(true)]
-[assembly: AssemblyKeyFile("c5.pub")]
-
-[assembly: CLSCompliant(true)]
+/* + Copyright (c) 2003-2007 Niels Kokholm and Peter Sestoft + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("C5: Copenhagen Comprehensive Collection Classes for CLI")] +[assembly: AssemblyDescription("This is a build of release 1.1.0")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("(c) 2003-2008 Niels Kokholm and Peter Sestoft")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.1.0")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\<configuration>. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(true)] +[assembly: AssemblyKeyFile("c5.pub")] + +[assembly: CLSCompliant(true)] diff --git a/mcs/class/Mono.C5/C5/Attributes.cs b/mcs/class/Mono.C5/C5/Attributes.cs index fe5a0da3a9d..95ccba036b5 100644 --- a/mcs/class/Mono.C5/C5/Attributes.cs +++ b/mcs/class/Mono.C5/C5/Attributes.cs @@ -1,48 +1,48 @@ -/*
- Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-using System;
-using System.Diagnostics;
-using SCG = System.Collections.Generic;
-namespace C5
-{
- /// <summary>
- /// A custom attribute to mark methods and properties as being tested
- /// sufficiently in the regression test suite.
- /// </summary>
- [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
- public sealed class TestedAttribute : Attribute
- {
-
- /// <summary>
- /// Optional reference to test case
- /// </summary>
- [Tested]
- public string via;
-
-
- /// <summary>
- /// Pretty print attribute value
- /// </summary>
- /// <returns>"Tested via " + via</returns>
- [Tested]
- public override string ToString() { return "Tested via " + via; }
- }
+/* + Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +using System; +using System.Diagnostics; +using SCG = System.Collections.Generic; +namespace C5 +{ + /// <summary> + /// A custom attribute to mark methods and properties as being tested + /// sufficiently in the regression test suite. + /// </summary> + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public sealed class TestedAttribute : Attribute + { + + /// <summary> + /// Optional reference to test case + /// </summary> + [Tested] + public string via; + + + /// <summary> + /// Pretty print attribute value + /// </summary> + /// <returns>"Tested via " + via</returns> + [Tested] + public override string ToString() { return "Tested via " + via; } + } }
\ No newline at end of file diff --git a/mcs/class/Mono.C5/C5/Builtin.cs b/mcs/class/Mono.C5/C5/Builtin.cs index fdc656a75f8..ac115b09390 100644 --- a/mcs/class/Mono.C5/C5/Builtin.cs +++ b/mcs/class/Mono.C5/C5/Builtin.cs @@ -1,574 +1,574 @@ -/*
- Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-using System;
-using System.Diagnostics;
-using SCG = System.Collections.Generic;
-namespace C5
-{
- #region char comparer and equality comparer
- class CharComparer : SCG.IComparer<char>
- {
- public int Compare(char item1, char item2) {
- return item1 > item2 ? 1 : item1 < item2 ? -1 : 0;
- }
- }
-
- /// <summary>
- /// An equality comparer for type char, also known as System.Char.
- /// </summary>
- public class CharEqualityComparer : SCG.IEqualityComparer<char>
- {
- static CharEqualityComparer cached = new CharEqualityComparer();
- CharEqualityComparer() { }
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public static CharEqualityComparer Default { get { return cached ?? (cached = new CharEqualityComparer()); } }
-
- /// <summary>
- /// Get the hash code of this char
- /// </summary>
- /// <param name="item">The char</param>
- /// <returns>The same</returns>
- public int GetHashCode(char item) { return item.GetHashCode(); }
-
-
- /// <summary>
- /// Check if two chars are equal
- /// </summary>
- /// <param name="item1">first char</param>
- /// <param name="item2">second char</param>
- /// <returns>True if equal</returns>
- public bool Equals(char item1, char item2) { return item1 == item2; }
- }
- #endregion
-
- #region sbyte comparer and equality comparer
- [Serializable]
- class SByteComparer : SCG.IComparer<sbyte>
- {
- [Tested]
- public int Compare(sbyte item1, sbyte item2) {
- return item1 > item2 ? 1 : item1 < item2 ? -1 : 0;
- }
- }
-
- /// <summary>
- /// An equality comparer for type sbyte, also known as System.SByte.
- /// <para>This class is a singleton and the instance can be accessed
- /// via the static <see cref="P:C5.SByteEqualityComparer.Default"/> property</para>
- /// </summary>
- [Serializable]
- public class SByteEqualityComparer : SCG.IEqualityComparer<sbyte>
- {
- static SByteEqualityComparer cached;
- SByteEqualityComparer() { }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- [Tested]
- public static SByteEqualityComparer Default { get { return cached ?? (cached = new SByteEqualityComparer()); } }
- /// <summary>
- /// Get the hash code of this sbyte, that is, itself
- /// </summary>
- /// <param name="item">The sbyte</param>
- /// <returns>The same</returns>
- [Tested]
- public int GetHashCode(sbyte item) { return item.GetHashCode(); }
-
-
- /// <summary>
- /// Determine whether two sbytes are equal
- /// </summary>
- /// <param name="item1">first sbyte</param>
- /// <param name="item2">second sbyte</param>
- /// <returns>True if equal</returns>
- [Tested]
- public bool Equals(sbyte item1, sbyte item2) { return item1 == item2; }
- }
-
- #endregion
-
- #region byte comparer and equality comparer
- class ByteComparer : SCG.IComparer<byte>
- {
- public int Compare(byte item1, byte item2) {
- return item1 > item2 ? 1 : item1 < item2 ? -1 : 0;
- }
- }
-
- /// <summary>
- /// An equality comparer for type byte, also known as System.Byte.
- /// <para>This class is a singleton and the instance can be accessed
- /// via the <see cref="P:C5.ByteEqualityComparer.Default"/> property</para>
- /// </summary>
- public class ByteEqualityComparer : SCG.IEqualityComparer<byte>
- {
- static ByteEqualityComparer cached = new ByteEqualityComparer();
- ByteEqualityComparer() { }
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public static ByteEqualityComparer Default { get { return cached ?? (cached = new ByteEqualityComparer()); } }
- /// <summary>
- /// Get the hash code of this byte, i.e. itself
- /// </summary>
- /// <param name="item">The byte</param>
- /// <returns>The same</returns>
- public int GetHashCode(byte item) { return item.GetHashCode(); }
-
- /// <summary>
- /// Check if two bytes are equal
- /// </summary>
- /// <param name="item1">first byte</param>
- /// <param name="item2">second byte</param>
- /// <returns>True if equal</returns>
- public bool Equals(byte item1, byte item2) { return item1 == item2; }
- }
- #endregion
-
- #region short comparer and equality comparer
- [Serializable]
- class ShortComparer : SCG.IComparer<short>
- {
- [Tested]
- public int Compare(short item1, short item2) {
- return item1 > item2 ? 1 : item1 < item2 ? -1 : 0;
- }
- }
-
- /// <summary>
- /// An equality comparer for type short, also known as System.Int16.
- /// <para>This class is a singleton and the instance can be accessed
- /// via the static <see cref="P:C5.ShortEqualityComparer.Default"/> property</para>
- /// </summary>
- [Serializable]
- public class ShortEqualityComparer : SCG.IEqualityComparer<short>
- {
- static ShortEqualityComparer cached;
- ShortEqualityComparer() { }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- [Tested]
- public static ShortEqualityComparer Default { get { return cached ?? (cached = new ShortEqualityComparer()); } }
- /// <summary>
- /// Get the hash code of this short, that is, itself
- /// </summary>
- /// <param name="item">The short</param>
- /// <returns>The same</returns>
- [Tested]
- public int GetHashCode(short item) { return item.GetHashCode(); }
-
-
- /// <summary>
- /// Determine whether two shorts are equal
- /// </summary>
- /// <param name="item1">first short</param>
- /// <param name="item2">second short</param>
- /// <returns>True if equal</returns>
- [Tested]
- public bool Equals(short item1, short item2) { return item1 == item2; }
- }
-
- #endregion
-
- #region ushort comparer and equality comparer
- [Serializable]
- class UShortComparer : SCG.IComparer<ushort>
- {
- [Tested]
- public int Compare(ushort item1, ushort item2)
- {
- return item1 > item2 ? 1 : item1 < item2 ? -1 : 0;
- }
- }
-
- /// <summary>
- /// An equality comparer for type ushort, also known as System.UInt16.
- /// <para>This class is a singleton and the instance can be accessed
- /// via the static <see cref="P:C5.UShortEqualityComparer.Default"/> property</para>
- /// </summary>
- [Serializable]
- public class UShortEqualityComparer : SCG.IEqualityComparer<ushort>
- {
- static UShortEqualityComparer cached;
- UShortEqualityComparer() { }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- [Tested]
- public static UShortEqualityComparer Default { get { return cached ?? (cached = new UShortEqualityComparer()); } }
- /// <summary>
- /// Get the hash code of this ushort, that is, itself
- /// </summary>
- /// <param name="item">The ushort</param>
- /// <returns>The same</returns>
- [Tested]
- public int GetHashCode(ushort item) { return item.GetHashCode(); }
-
-
- /// <summary>
- /// Determine whether two ushorts are equal
- /// </summary>
- /// <param name="item1">first ushort</param>
- /// <param name="item2">second ushort</param>
- /// <returns>True if equal</returns>
- [Tested]
- public bool Equals(ushort item1, ushort item2) { return item1 == item2; }
- }
-
- #endregion
-
- #region int comparer and equality comparer
- [Serializable]
- class IntComparer : SCG.IComparer<int>
- {
- [Tested]
- public int Compare(int item1, int item2) {
- return item1 > item2 ? 1 : item1 < item2 ? -1 : 0;
- }
- }
-
- /// <summary>
- /// An equality comparer for type int, also known as System.Int32.
- /// <para>This class is a singleton and the instance can be accessed
- /// via the static <see cref="P:C5.IntEqualityComparer.Default"/> property</para>
- /// </summary>
- [Serializable]
- public class IntEqualityComparer : SCG.IEqualityComparer<int>
- {
- static IntEqualityComparer cached;
- IntEqualityComparer() { }
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- [Tested]
- public static IntEqualityComparer Default { get { return cached ?? (cached = new IntEqualityComparer()); } }
- /// <summary>
- /// Get the hash code of this integer, that is, itself
- /// </summary>
- /// <param name="item">The integer</param>
- /// <returns>The same</returns>
- [Tested]
- public int GetHashCode(int item) { return item; }
-
-
- /// <summary>
- /// Determine whether two integers are equal
- /// </summary>
- /// <param name="item1">first integer</param>
- /// <param name="item2">second integer</param>
- /// <returns>True if equal</returns>
- [Tested]
- public bool Equals(int item1, int item2) { return item1 == item2; }
- }
-
- #endregion
-
- #region uint comparer and equality comparer
- [Serializable]
- class UIntComparer : SCG.IComparer<uint>
- {
- [Tested]
- public int Compare(uint item1, uint item2)
- {
- return item1 > item2 ? 1 : item1 < item2 ? -1 : 0;
- }
- }
-
- /// <summary>
- /// An equality comparer for type uint, also known as System.UInt32.
- /// <para>This class is a singleton and the instance can be accessed
- /// via the static <see cref="P:C5.UIntEqualityComparer.Default"/> property</para>
- /// </summary>
- [Serializable]
- public class UIntEqualityComparer : SCG.IEqualityComparer<uint>
- {
- static UIntEqualityComparer cached;
- UIntEqualityComparer() { }
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- [Tested]
- public static UIntEqualityComparer Default { get { return cached ?? (cached = new UIntEqualityComparer()); } }
- /// <summary>
- /// Get the hash code of this unsigned integer
- /// </summary>
- /// <param name="item">The integer</param>
- /// <returns>The same bit pattern as a signed integer</returns>
- [Tested]
- public int GetHashCode(uint item) { return item.GetHashCode(); }
-
-
- /// <summary>
- /// Determine whether two unsigned integers are equal
- /// </summary>
- /// <param name="item1">first unsigned integer</param>
- /// <param name="item2">second unsigned integer</param>
- /// <returns>True if equal</returns>
- [Tested]
- public bool Equals(uint item1, uint item2) { return item1 == item2; }
- }
-
- #endregion
-
- #region long comparer and equality comparer
- [Serializable]
- class LongComparer : SCG.IComparer<long>
- {
- [Tested]
- public int Compare(long item1, long item2)
- {
- return item1 > item2 ? 1 : item1 < item2 ? -1 : 0;
- }
- }
-
- /// <summary>
- /// An equality comparer for type long, also known as System.Int64.
- /// <para>This class is a singleton and the instance can be accessed
- /// via the static <see cref="P:C5.LongEqualityComparer.Default"/> property</para>
- /// </summary>
- [Serializable]
- public class LongEqualityComparer : SCG.IEqualityComparer<long>
- {
- static LongEqualityComparer cached;
- LongEqualityComparer() { }
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- [Tested]
- public static LongEqualityComparer Default { get { return cached ?? (cached = new LongEqualityComparer()); } }
- /// <summary>
- /// Get the hash code of this long integer
- /// </summary>
- /// <param name="item">The long integer</param>
- /// <returns>The hash code</returns>
- [Tested]
- public int GetHashCode(long item) { return item.GetHashCode(); }
-
-
- /// <summary>
- /// Determine whether two long integers are equal
- /// </summary>
- /// <param name="item1">first long integer</param>
- /// <param name="item2">second long integer</param>
- /// <returns>True if equal</returns>
- [Tested]
- public bool Equals(long item1, long item2) { return item1 == item2; }
- }
-
- #endregion
-
- #region ulong comparer and equality comparer
- [Serializable]
- class ULongComparer : SCG.IComparer<ulong>
- {
- [Tested]
- public int Compare(ulong item1, ulong item2)
- {
- return item1 > item2 ? 1 : item1 < item2 ? -1 : 0;
- }
- }
-
- /// <summary>
- /// An equality comparer for type uint, also known as System.UInt64.
- /// <para>This class is a singleton and the instance can be accessed
- /// via the static <see cref="P:C5.ULongEqualityComparer.Default"/> property</para>
- /// </summary>
- [Serializable]
- public class ULongEqualityComparer : SCG.IEqualityComparer<ulong>
- {
- static ULongEqualityComparer cached;
- ULongEqualityComparer() { }
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- [Tested]
- public static ULongEqualityComparer Default { get { return cached ?? (cached = new ULongEqualityComparer()); } }
- /// <summary>
- /// Get the hash code of this unsigned long integer
- /// </summary>
- /// <param name="item">The unsigned long integer</param>
- /// <returns>The hash code</returns>
- [Tested]
- public int GetHashCode(ulong item) { return item.GetHashCode(); }
-
-
- /// <summary>
- /// Determine whether two unsigned long integers are equal
- /// </summary>
- /// <param name="item1">first unsigned long integer</param>
- /// <param name="item2">second unsigned long integer</param>
- /// <returns>True if equal</returns>
- [Tested]
- public bool Equals(ulong item1, ulong item2) { return item1 == item2; }
- }
-
- #endregion
-
- #region float comparer and equality comparer
- class FloatComparer : SCG.IComparer<float>
- {
- public int Compare(float item1, float item2)
- {
- return item1 > item2 ? 1 : item1 < item2 ? -1 : 0;
- }
- }
-
- /// <summary>
- /// An equality comparer for type float, also known as System.Single.
- /// <para>This class is a singleton and the instance can be accessed
- /// via the static <see cref="P:C5.FloatEqualityComparer.Default"/> property</para>
- /// </summary>
- public class FloatEqualityComparer : SCG.IEqualityComparer<float>
- {
- static FloatEqualityComparer cached;
- FloatEqualityComparer() { }
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- [Tested]
- public static FloatEqualityComparer Default { get { return cached ?? (cached = new FloatEqualityComparer()); } }
- /// <summary>
- /// Get the hash code of this float
- /// </summary>
- /// <param name="item">The float</param>
- /// <returns>The same</returns>
- [Tested]
- public int GetHashCode(float item) { return item.GetHashCode(); }
-
-
- /// <summary>
- /// Check if two floats are equal
- /// </summary>
- /// <param name="item1">first float</param>
- /// <param name="item2">second float</param>
- /// <returns>True if equal</returns>
- [Tested]
- public bool Equals(float item1, float item2) { return item1 == item2; }
- }
- #endregion
-
- #region double comparer and equality comparer
- class DoubleComparer : SCG.IComparer<double>
- {
- public int Compare(double item1, double item2) {
- return item1 > item2 ? 1 : item1 < item2 ? -1 : 0;
- }
- }
-
- /// <summary>
- /// An equality comparer for type double, also known as System.Double.
- /// <para>This class is a singleton and the instance can be accessed
- /// via the static <see cref="P:C5.DoubleEqualityComparer.Default"/> property</para>
- /// </summary>
- public class DoubleEqualityComparer : SCG.IEqualityComparer<double>
- {
- static DoubleEqualityComparer cached;
- DoubleEqualityComparer() { }
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- [Tested]
- public static DoubleEqualityComparer Default { get { return cached ?? (cached = new DoubleEqualityComparer()); } }
- /// <summary>
- /// Get the hash code of this double
- /// </summary>
- /// <param name="item">The double</param>
- /// <returns>The same</returns>
- [Tested]
- public int GetHashCode(double item) { return item.GetHashCode(); }
-
-
- /// <summary>
- /// Check if two doubles are equal
- /// </summary>
- /// <param name="item1">first double</param>
- /// <param name="item2">second double</param>
- /// <returns>True if equal</returns>
- [Tested]
- public bool Equals(double item1, double item2) { return item1 == item2; }
- }
- #endregion
-
- #region decimal comparer and equality comparer
- [Serializable]
- class DecimalComparer : SCG.IComparer<decimal>
- {
- [Tested]
- public int Compare(decimal item1, decimal item2)
- {
- return item1 > item2 ? 1 : item1 < item2 ? -1 : 0;
- }
- }
-
- /// <summary>
- /// An equality comparer for type decimal, also known as System.Decimal.
- /// <para>This class is a singleton and the instance can be accessed
- /// via the static <see cref="P:C5.DecimalEqualityComparer.Default"/> property</para>
- /// </summary>
- [Serializable]
- public class DecimalEqualityComparer : SCG.IEqualityComparer<decimal>
- {
- static DecimalEqualityComparer cached;
- DecimalEqualityComparer() { }
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- [Tested]
- public static DecimalEqualityComparer Default { get { return cached ?? (cached = new DecimalEqualityComparer()); } }
- /// <summary>
- /// Get the hash code of this decimal.
- /// </summary>
- /// <param name="item">The decimal</param>
- /// <returns>The hash code</returns>
- [Tested]
- public int GetHashCode(decimal item) { return item.GetHashCode(); }
-
-
- /// <summary>
- /// Determine whether two decimals are equal
- /// </summary>
- /// <param name="item1">first decimal</param>
- /// <param name="item2">second decimal</param>
- /// <returns>True if equal</returns>
- [Tested]
- public bool Equals(decimal item1, decimal item2) { return item1 == item2; }
- }
-
- #endregion
+/* + Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +using System; +using System.Diagnostics; +using SCG = System.Collections.Generic; +namespace C5 +{ + #region char comparer and equality comparer + class CharComparer : SCG.IComparer<char> + { + public int Compare(char item1, char item2) { + return item1 > item2 ? 1 : item1 < item2 ? -1 : 0; + } + } + + /// <summary> + /// An equality comparer for type char, also known as System.Char. + /// </summary> + public class CharEqualityComparer : SCG.IEqualityComparer<char> + { + static CharEqualityComparer cached = new CharEqualityComparer(); + CharEqualityComparer() { } + /// <summary> + /// + /// </summary> + /// <value></value> + public static CharEqualityComparer Default { get { return cached ?? (cached = new CharEqualityComparer()); } } + + /// <summary> + /// Get the hash code of this char + /// </summary> + /// <param name="item">The char</param> + /// <returns>The same</returns> + public int GetHashCode(char item) { return item.GetHashCode(); } + + + /// <summary> + /// Check if two chars are equal + /// </summary> + /// <param name="item1">first char</param> + /// <param name="item2">second char</param> + /// <returns>True if equal</returns> + public bool Equals(char item1, char item2) { return item1 == item2; } + } + #endregion + + #region sbyte comparer and equality comparer + [Serializable] + class SByteComparer : SCG.IComparer<sbyte> + { + [Tested] + public int Compare(sbyte item1, sbyte item2) { + return item1 > item2 ? 1 : item1 < item2 ? -1 : 0; + } + } + + /// <summary> + /// An equality comparer for type sbyte, also known as System.SByte. + /// <para>This class is a singleton and the instance can be accessed + /// via the static <see cref="P:C5.SByteEqualityComparer.Default"/> property</para> + /// </summary> + [Serializable] + public class SByteEqualityComparer : SCG.IEqualityComparer<sbyte> + { + static SByteEqualityComparer cached; + SByteEqualityComparer() { } + + /// <summary> + /// + /// </summary> + /// <value></value> + [Tested] + public static SByteEqualityComparer Default { get { return cached ?? (cached = new SByteEqualityComparer()); } } + /// <summary> + /// Get the hash code of this sbyte, that is, itself + /// </summary> + /// <param name="item">The sbyte</param> + /// <returns>The same</returns> + [Tested] + public int GetHashCode(sbyte item) { return item.GetHashCode(); } + + + /// <summary> + /// Determine whether two sbytes are equal + /// </summary> + /// <param name="item1">first sbyte</param> + /// <param name="item2">second sbyte</param> + /// <returns>True if equal</returns> + [Tested] + public bool Equals(sbyte item1, sbyte item2) { return item1 == item2; } + } + + #endregion + + #region byte comparer and equality comparer + class ByteComparer : SCG.IComparer<byte> + { + public int Compare(byte item1, byte item2) { + return item1 > item2 ? 1 : item1 < item2 ? -1 : 0; + } + } + + /// <summary> + /// An equality comparer for type byte, also known as System.Byte. + /// <para>This class is a singleton and the instance can be accessed + /// via the <see cref="P:C5.ByteEqualityComparer.Default"/> property</para> + /// </summary> + public class ByteEqualityComparer : SCG.IEqualityComparer<byte> + { + static ByteEqualityComparer cached = new ByteEqualityComparer(); + ByteEqualityComparer() { } + /// <summary> + /// + /// </summary> + /// <value></value> + public static ByteEqualityComparer Default { get { return cached ?? (cached = new ByteEqualityComparer()); } } + /// <summary> + /// Get the hash code of this byte, i.e. itself + /// </summary> + /// <param name="item">The byte</param> + /// <returns>The same</returns> + public int GetHashCode(byte item) { return item.GetHashCode(); } + + /// <summary> + /// Check if two bytes are equal + /// </summary> + /// <param name="item1">first byte</param> + /// <param name="item2">second byte</param> + /// <returns>True if equal</returns> + public bool Equals(byte item1, byte item2) { return item1 == item2; } + } + #endregion + + #region short comparer and equality comparer + [Serializable] + class ShortComparer : SCG.IComparer<short> + { + [Tested] + public int Compare(short item1, short item2) { + return item1 > item2 ? 1 : item1 < item2 ? -1 : 0; + } + } + + /// <summary> + /// An equality comparer for type short, also known as System.Int16. + /// <para>This class is a singleton and the instance can be accessed + /// via the static <see cref="P:C5.ShortEqualityComparer.Default"/> property</para> + /// </summary> + [Serializable] + public class ShortEqualityComparer : SCG.IEqualityComparer<short> + { + static ShortEqualityComparer cached; + ShortEqualityComparer() { } + + /// <summary> + /// + /// </summary> + /// <value></value> + [Tested] + public static ShortEqualityComparer Default { get { return cached ?? (cached = new ShortEqualityComparer()); } } + /// <summary> + /// Get the hash code of this short, that is, itself + /// </summary> + /// <param name="item">The short</param> + /// <returns>The same</returns> + [Tested] + public int GetHashCode(short item) { return item.GetHashCode(); } + + + /// <summary> + /// Determine whether two shorts are equal + /// </summary> + /// <param name="item1">first short</param> + /// <param name="item2">second short</param> + /// <returns>True if equal</returns> + [Tested] + public bool Equals(short item1, short item2) { return item1 == item2; } + } + + #endregion + + #region ushort comparer and equality comparer + [Serializable] + class UShortComparer : SCG.IComparer<ushort> + { + [Tested] + public int Compare(ushort item1, ushort item2) + { + return item1 > item2 ? 1 : item1 < item2 ? -1 : 0; + } + } + + /// <summary> + /// An equality comparer for type ushort, also known as System.UInt16. + /// <para>This class is a singleton and the instance can be accessed + /// via the static <see cref="P:C5.UShortEqualityComparer.Default"/> property</para> + /// </summary> + [Serializable] + public class UShortEqualityComparer : SCG.IEqualityComparer<ushort> + { + static UShortEqualityComparer cached; + UShortEqualityComparer() { } + + /// <summary> + /// + /// </summary> + /// <value></value> + [Tested] + public static UShortEqualityComparer Default { get { return cached ?? (cached = new UShortEqualityComparer()); } } + /// <summary> + /// Get the hash code of this ushort, that is, itself + /// </summary> + /// <param name="item">The ushort</param> + /// <returns>The same</returns> + [Tested] + public int GetHashCode(ushort item) { return item.GetHashCode(); } + + + /// <summary> + /// Determine whether two ushorts are equal + /// </summary> + /// <param name="item1">first ushort</param> + /// <param name="item2">second ushort</param> + /// <returns>True if equal</returns> + [Tested] + public bool Equals(ushort item1, ushort item2) { return item1 == item2; } + } + + #endregion + + #region int comparer and equality comparer + [Serializable] + class IntComparer : SCG.IComparer<int> + { + [Tested] + public int Compare(int item1, int item2) { + return item1 > item2 ? 1 : item1 < item2 ? -1 : 0; + } + } + + /// <summary> + /// An equality comparer for type int, also known as System.Int32. + /// <para>This class is a singleton and the instance can be accessed + /// via the static <see cref="P:C5.IntEqualityComparer.Default"/> property</para> + /// </summary> + [Serializable] + public class IntEqualityComparer : SCG.IEqualityComparer<int> + { + static IntEqualityComparer cached; + IntEqualityComparer() { } + /// <summary> + /// + /// </summary> + /// <value></value> + [Tested] + public static IntEqualityComparer Default { get { return cached ?? (cached = new IntEqualityComparer()); } } + /// <summary> + /// Get the hash code of this integer, that is, itself + /// </summary> + /// <param name="item">The integer</param> + /// <returns>The same</returns> + [Tested] + public int GetHashCode(int item) { return item; } + + + /// <summary> + /// Determine whether two integers are equal + /// </summary> + /// <param name="item1">first integer</param> + /// <param name="item2">second integer</param> + /// <returns>True if equal</returns> + [Tested] + public bool Equals(int item1, int item2) { return item1 == item2; } + } + + #endregion + + #region uint comparer and equality comparer + [Serializable] + class UIntComparer : SCG.IComparer<uint> + { + [Tested] + public int Compare(uint item1, uint item2) + { + return item1 > item2 ? 1 : item1 < item2 ? -1 : 0; + } + } + + /// <summary> + /// An equality comparer for type uint, also known as System.UInt32. + /// <para>This class is a singleton and the instance can be accessed + /// via the static <see cref="P:C5.UIntEqualityComparer.Default"/> property</para> + /// </summary> + [Serializable] + public class UIntEqualityComparer : SCG.IEqualityComparer<uint> + { + static UIntEqualityComparer cached; + UIntEqualityComparer() { } + /// <summary> + /// + /// </summary> + /// <value></value> + [Tested] + public static UIntEqualityComparer Default { get { return cached ?? (cached = new UIntEqualityComparer()); } } + /// <summary> + /// Get the hash code of this unsigned integer + /// </summary> + /// <param name="item">The integer</param> + /// <returns>The same bit pattern as a signed integer</returns> + [Tested] + public int GetHashCode(uint item) { return item.GetHashCode(); } + + + /// <summary> + /// Determine whether two unsigned integers are equal + /// </summary> + /// <param name="item1">first unsigned integer</param> + /// <param name="item2">second unsigned integer</param> + /// <returns>True if equal</returns> + [Tested] + public bool Equals(uint item1, uint item2) { return item1 == item2; } + } + + #endregion + + #region long comparer and equality comparer + [Serializable] + class LongComparer : SCG.IComparer<long> + { + [Tested] + public int Compare(long item1, long item2) + { + return item1 > item2 ? 1 : item1 < item2 ? -1 : 0; + } + } + + /// <summary> + /// An equality comparer for type long, also known as System.Int64. + /// <para>This class is a singleton and the instance can be accessed + /// via the static <see cref="P:C5.LongEqualityComparer.Default"/> property</para> + /// </summary> + [Serializable] + public class LongEqualityComparer : SCG.IEqualityComparer<long> + { + static LongEqualityComparer cached; + LongEqualityComparer() { } + /// <summary> + /// + /// </summary> + /// <value></value> + [Tested] + public static LongEqualityComparer Default { get { return cached ?? (cached = new LongEqualityComparer()); } } + /// <summary> + /// Get the hash code of this long integer + /// </summary> + /// <param name="item">The long integer</param> + /// <returns>The hash code</returns> + [Tested] + public int GetHashCode(long item) { return item.GetHashCode(); } + + + /// <summary> + /// Determine whether two long integers are equal + /// </summary> + /// <param name="item1">first long integer</param> + /// <param name="item2">second long integer</param> + /// <returns>True if equal</returns> + [Tested] + public bool Equals(long item1, long item2) { return item1 == item2; } + } + + #endregion + + #region ulong comparer and equality comparer + [Serializable] + class ULongComparer : SCG.IComparer<ulong> + { + [Tested] + public int Compare(ulong item1, ulong item2) + { + return item1 > item2 ? 1 : item1 < item2 ? -1 : 0; + } + } + + /// <summary> + /// An equality comparer for type uint, also known as System.UInt64. + /// <para>This class is a singleton and the instance can be accessed + /// via the static <see cref="P:C5.ULongEqualityComparer.Default"/> property</para> + /// </summary> + [Serializable] + public class ULongEqualityComparer : SCG.IEqualityComparer<ulong> + { + static ULongEqualityComparer cached; + ULongEqualityComparer() { } + /// <summary> + /// + /// </summary> + /// <value></value> + [Tested] + public static ULongEqualityComparer Default { get { return cached ?? (cached = new ULongEqualityComparer()); } } + /// <summary> + /// Get the hash code of this unsigned long integer + /// </summary> + /// <param name="item">The unsigned long integer</param> + /// <returns>The hash code</returns> + [Tested] + public int GetHashCode(ulong item) { return item.GetHashCode(); } + + + /// <summary> + /// Determine whether two unsigned long integers are equal + /// </summary> + /// <param name="item1">first unsigned long integer</param> + /// <param name="item2">second unsigned long integer</param> + /// <returns>True if equal</returns> + [Tested] + public bool Equals(ulong item1, ulong item2) { return item1 == item2; } + } + + #endregion + + #region float comparer and equality comparer + class FloatComparer : SCG.IComparer<float> + { + public int Compare(float item1, float item2) + { + return item1 > item2 ? 1 : item1 < item2 ? -1 : 0; + } + } + + /// <summary> + /// An equality comparer for type float, also known as System.Single. + /// <para>This class is a singleton and the instance can be accessed + /// via the static <see cref="P:C5.FloatEqualityComparer.Default"/> property</para> + /// </summary> + public class FloatEqualityComparer : SCG.IEqualityComparer<float> + { + static FloatEqualityComparer cached; + FloatEqualityComparer() { } + /// <summary> + /// + /// </summary> + /// <value></value> + [Tested] + public static FloatEqualityComparer Default { get { return cached ?? (cached = new FloatEqualityComparer()); } } + /// <summary> + /// Get the hash code of this float + /// </summary> + /// <param name="item">The float</param> + /// <returns>The same</returns> + [Tested] + public int GetHashCode(float item) { return item.GetHashCode(); } + + + /// <summary> + /// Check if two floats are equal + /// </summary> + /// <param name="item1">first float</param> + /// <param name="item2">second float</param> + /// <returns>True if equal</returns> + [Tested] + public bool Equals(float item1, float item2) { return item1 == item2; } + } + #endregion + + #region double comparer and equality comparer + class DoubleComparer : SCG.IComparer<double> + { + public int Compare(double item1, double item2) { + return item1 > item2 ? 1 : item1 < item2 ? -1 : 0; + } + } + + /// <summary> + /// An equality comparer for type double, also known as System.Double. + /// <para>This class is a singleton and the instance can be accessed + /// via the static <see cref="P:C5.DoubleEqualityComparer.Default"/> property</para> + /// </summary> + public class DoubleEqualityComparer : SCG.IEqualityComparer<double> + { + static DoubleEqualityComparer cached; + DoubleEqualityComparer() { } + /// <summary> + /// + /// </summary> + /// <value></value> + [Tested] + public static DoubleEqualityComparer Default { get { return cached ?? (cached = new DoubleEqualityComparer()); } } + /// <summary> + /// Get the hash code of this double + /// </summary> + /// <param name="item">The double</param> + /// <returns>The same</returns> + [Tested] + public int GetHashCode(double item) { return item.GetHashCode(); } + + + /// <summary> + /// Check if two doubles are equal + /// </summary> + /// <param name="item1">first double</param> + /// <param name="item2">second double</param> + /// <returns>True if equal</returns> + [Tested] + public bool Equals(double item1, double item2) { return item1 == item2; } + } + #endregion + + #region decimal comparer and equality comparer + [Serializable] + class DecimalComparer : SCG.IComparer<decimal> + { + [Tested] + public int Compare(decimal item1, decimal item2) + { + return item1 > item2 ? 1 : item1 < item2 ? -1 : 0; + } + } + + /// <summary> + /// An equality comparer for type decimal, also known as System.Decimal. + /// <para>This class is a singleton and the instance can be accessed + /// via the static <see cref="P:C5.DecimalEqualityComparer.Default"/> property</para> + /// </summary> + [Serializable] + public class DecimalEqualityComparer : SCG.IEqualityComparer<decimal> + { + static DecimalEqualityComparer cached; + DecimalEqualityComparer() { } + /// <summary> + /// + /// </summary> + /// <value></value> + [Tested] + public static DecimalEqualityComparer Default { get { return cached ?? (cached = new DecimalEqualityComparer()); } } + /// <summary> + /// Get the hash code of this decimal. + /// </summary> + /// <param name="item">The decimal</param> + /// <returns>The hash code</returns> + [Tested] + public int GetHashCode(decimal item) { return item.GetHashCode(); } + + + /// <summary> + /// Determine whether two decimals are equal + /// </summary> + /// <param name="item1">first decimal</param> + /// <param name="item2">second decimal</param> + /// <returns>True if equal</returns> + [Tested] + public bool Equals(decimal item1, decimal item2) { return item1 == item2; } + } + + #endregion }
\ No newline at end of file diff --git a/mcs/class/Mono.C5/C5/Collections.cs b/mcs/class/Mono.C5/C5/Collections.cs index ee297d8b2ab..2fc7c55475a 100644 --- a/mcs/class/Mono.C5/C5/Collections.cs +++ b/mcs/class/Mono.C5/C5/Collections.cs @@ -1,1512 +1,1512 @@ -/*
- Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-
-#define IMPROVED_COLLECTION_HASHFUNCTION
-
-using System;
-using System.Diagnostics;
-using SCG = System.Collections.Generic;
-namespace C5
-{
- /// <summary>
- /// A base class for implementing an IEnumerable<T>
- /// </summary>
- [Serializable]
- public abstract class EnumerableBase<T> : SCG.IEnumerable<T>
- {
- /// <summary>
- /// Create an enumerator for this collection.
- /// </summary>
- /// <returns>The enumerator</returns>
- public abstract SCG.IEnumerator<T> GetEnumerator();
-
- /// <summary>
- /// Count the number of items in an enumerable by enumeration
- /// </summary>
- /// <param name="items">The enumerable to count</param>
- /// <returns>The size of the enumerable</returns>
- [Tested]
- protected static int countItems(SCG.IEnumerable<T> items)
- {
- ICollectionValue<T> jtems = items as ICollectionValue<T>;
-
- if (jtems != null)
- return jtems.Count;
-
- int count = 0;
-
- using (SCG.IEnumerator<T> e = items.GetEnumerator())
- while (e.MoveNext()) count++;
-
- return count;
- }
-
- #region IEnumerable Members
-
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
-
- #endregion
- }
-
-
- /// <summary>
- /// Base class for classes implementing ICollectionValue[T]
- /// </summary>
- [Serializable]
- public abstract class CollectionValueBase<T> : EnumerableBase<T>, ICollectionValue<T>, IShowable
- {
- #region Event handling
- EventBlock<T> eventBlock;
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public virtual EventTypeEnum ListenableEvents { get { return 0; } }
-
- /// <summary>
- /// A flag bitmap of the events currently subscribed to by this collection.
- /// </summary>
- /// <value></value>
- public virtual EventTypeEnum ActiveEvents { get { return eventBlock == null ? 0 : eventBlock.events; } }
-
- private void checkWillListen(EventTypeEnum eventType)
- {
- if ((ListenableEvents & eventType) == 0)
- throw new UnlistenableEventException();
- }
-
- /// <summary>
- /// The change event. Will be raised for every change operation on the collection.
- /// </summary>
- public virtual event CollectionChangedHandler<T> CollectionChanged
- {
- add { checkWillListen(EventTypeEnum.Changed); (eventBlock ?? (eventBlock = new EventBlock<T>())).CollectionChanged += value; }
- remove
- {
- checkWillListen(EventTypeEnum.Changed);
- if (eventBlock != null)
- {
- eventBlock.CollectionChanged -= value;
- if (eventBlock.events == 0) eventBlock = null;
- }
- }
- }
- /// <summary>
- /// Fire the CollectionChanged event
- /// </summary>
- protected virtual void raiseCollectionChanged()
- { if (eventBlock != null) eventBlock.raiseCollectionChanged(this); }
-
- /// <summary>
- /// The clear event. Will be raised for every Clear operation on the collection.
- /// </summary>
- public virtual event CollectionClearedHandler<T> CollectionCleared
- {
- add { checkWillListen(EventTypeEnum.Cleared); (eventBlock ?? (eventBlock = new EventBlock<T>())).CollectionCleared += value; }
- remove
- {
- checkWillListen(EventTypeEnum.Cleared);
- if (eventBlock != null)
- {
- eventBlock.CollectionCleared -= value;
- if (eventBlock.events == 0) eventBlock = null;
- }
- }
- }
- /// <summary>
- /// Fire the CollectionCleared event
- /// </summary>
- protected virtual void raiseCollectionCleared(bool full, int count)
- { if (eventBlock != null) eventBlock.raiseCollectionCleared(this, full, count); }
-
- /// <summary>
- /// Fire the CollectionCleared event
- /// </summary>
- protected virtual void raiseCollectionCleared(bool full, int count, int? offset)
- { if (eventBlock != null) eventBlock.raiseCollectionCleared(this, full, count, offset); }
-
- /// <summary>
- /// The item added event. Will be raised for every individual addition to the collection.
- /// </summary>
- public virtual event ItemsAddedHandler<T> ItemsAdded
- {
- add { checkWillListen(EventTypeEnum.Added); (eventBlock ?? (eventBlock = new EventBlock<T>())).ItemsAdded += value; }
- remove
- {
- checkWillListen(EventTypeEnum.Added);
- if (eventBlock != null)
- {
- eventBlock.ItemsAdded -= value;
- if (eventBlock.events == 0) eventBlock = null;
- }
- }
- }
- /// <summary>
- /// Fire the ItemsAdded event
- /// </summary>
- /// <param name="item">The item that was added</param>
- /// <param name="count"></param>
- protected virtual void raiseItemsAdded(T item, int count)
- { if (eventBlock != null) eventBlock.raiseItemsAdded(this, item, count); }
-
- /// <summary>
- /// The item removed event. Will be raised for every individual removal from the collection.
- /// </summary>
- public virtual event ItemsRemovedHandler<T> ItemsRemoved
- {
- add { checkWillListen(EventTypeEnum.Removed); (eventBlock ?? (eventBlock = new EventBlock<T>())).ItemsRemoved += value; }
- remove
- {
- checkWillListen(EventTypeEnum.Removed);
- if (eventBlock != null)
- {
- eventBlock.ItemsRemoved -= value;
- if (eventBlock.events == 0) eventBlock = null;
- }
- }
- }
- /// <summary>
- /// Fire the ItemsRemoved event
- /// </summary>
- /// <param name="item">The item that was removed</param>
- /// <param name="count"></param>
- protected virtual void raiseItemsRemoved(T item, int count)
- { if (eventBlock != null) eventBlock.raiseItemsRemoved(this, item, count); }
-
- /// <summary>
- /// The item added event. Will be raised for every individual addition to the collection.
- /// </summary>
- public virtual event ItemInsertedHandler<T> ItemInserted
- {
- add { checkWillListen(EventTypeEnum.Inserted); (eventBlock ?? (eventBlock = new EventBlock<T>())).ItemInserted += value; }
- remove
- {
- checkWillListen(EventTypeEnum.Inserted);
- if (eventBlock != null)
- {
- eventBlock.ItemInserted -= value;
- if (eventBlock.events == 0) eventBlock = null;
- }
- }
- }
- /// <summary>
- /// Fire the ItemInserted event
- /// </summary>
- /// <param name="item">The item that was added</param>
- /// <param name="index"></param>
- protected virtual void raiseItemInserted(T item, int index)
- { if (eventBlock != null) eventBlock.raiseItemInserted(this, item, index); }
-
- /// <summary>
- /// The item removed event. Will be raised for every individual removal from the collection.
- /// </summary>
- public virtual event ItemRemovedAtHandler<T> ItemRemovedAt
- {
- add { checkWillListen(EventTypeEnum.RemovedAt); (eventBlock ?? (eventBlock = new EventBlock<T>())).ItemRemovedAt += value; }
- remove
- {
- checkWillListen(EventTypeEnum.RemovedAt);
- if (eventBlock != null)
- {
- eventBlock.ItemRemovedAt -= value;
- if (eventBlock.events == 0) eventBlock = null;
- }
- }
- }
- /// <summary>
- /// Fire the ItemRemovedAt event
- /// </summary>
- /// <param name="item">The item that was removed</param>
- /// <param name="index"></param>
- protected virtual void raiseItemRemovedAt(T item, int index)
- { if (eventBlock != null) eventBlock.raiseItemRemovedAt(this, item, index); }
-
- #region Event support for IList
- /// <summary>
- ///
- /// </summary>
- /// <param name="index"></param>
- /// <param name="value"></param>
- /// <param name="item"></param>
- protected virtual void raiseForSetThis(int index, T value, T item)
- {
- if (ActiveEvents != 0)
- {
- raiseItemsRemoved(item, 1);
- raiseItemRemovedAt(item, index);
- raiseItemsAdded(value, 1);
- raiseItemInserted(value, index);
- raiseCollectionChanged();
- }
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="i"></param>
- /// <param name="item"></param>
- protected virtual void raiseForInsert(int i, T item)
- {
- if (ActiveEvents != 0)
- {
- raiseItemInserted(item, i);
- raiseItemsAdded(item, 1);
- raiseCollectionChanged();
- }
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- protected void raiseForRemove(T item)
- {
- if (ActiveEvents != 0)
- {
- raiseItemsRemoved(item, 1);
- raiseCollectionChanged();
- }
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <param name="count"></param>
- protected void raiseForRemove(T item, int count)
- {
- if (ActiveEvents != 0)
- {
- raiseItemsRemoved(item, count);
- raiseCollectionChanged();
- }
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="index"></param>
- /// <param name="item"></param>
- protected void raiseForRemoveAt(int index, T item)
- {
- if (ActiveEvents != 0)
- {
- raiseItemRemovedAt(item, index);
- raiseItemsRemoved(item, 1);
- raiseCollectionChanged();
- }
- }
-
- #endregion
-
- #region Event Support for ICollection
- /// <summary>
- ///
- /// </summary>
- /// <param name="newitem"></param>
- /// <param name="olditem"></param>
- protected virtual void raiseForUpdate(T newitem, T olditem)
- {
- if (ActiveEvents != 0)
- {
- raiseItemsRemoved(olditem, 1);
- raiseItemsAdded(newitem, 1);
- raiseCollectionChanged();
- }
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="newitem"></param>
- /// <param name="olditem"></param>
- /// <param name="count"></param>
- protected virtual void raiseForUpdate(T newitem, T olditem, int count)
- {
- if (ActiveEvents != 0)
- {
- raiseItemsRemoved(olditem, count);
- raiseItemsAdded(newitem, count);
- raiseCollectionChanged();
- }
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- protected virtual void raiseForAdd(T item)
- {
- if (ActiveEvents != 0)
- {
- raiseItemsAdded(item, 1);
- raiseCollectionChanged();
- }
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="wasRemoved"></param>
- protected virtual void raiseForRemoveAll(ICollectionValue<T> wasRemoved)
- {
- if ((ActiveEvents & EventTypeEnum.Removed) != 0)
- foreach (T item in wasRemoved)
- raiseItemsRemoved(item, 1);
- if (wasRemoved != null && wasRemoved.Count > 0)
- raiseCollectionChanged();
- }
-
- /// <summary>
- ///
- /// </summary>
- protected class RaiseForRemoveAllHandler
- {
- CollectionValueBase<T> collection;
- CircularQueue<T> wasRemoved;
- bool wasChanged = false;
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="collection"></param>
- public RaiseForRemoveAllHandler(CollectionValueBase<T> collection)
- {
- this.collection = collection;
- mustFireRemoved = (collection.ActiveEvents & EventTypeEnum.Removed) != 0;
- MustFire = (collection.ActiveEvents & (EventTypeEnum.Removed | EventTypeEnum.Changed)) != 0;
- }
-
- bool mustFireRemoved;
- /// <summary>
- ///
- /// </summary>
- public readonly bool MustFire;
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- public void Remove(T item)
- {
- if (mustFireRemoved)
- {
- if (wasRemoved == null)
- wasRemoved = new CircularQueue<T>();
- wasRemoved.Enqueue(item);
- }
- if (!wasChanged)
- wasChanged = true;
- }
-
- /// <summary>
- ///
- /// </summary>
- public void Raise()
- {
- if (wasRemoved != null)
- foreach (T item in wasRemoved)
- collection.raiseItemsRemoved(item, 1);
- if (wasChanged)
- collection.raiseCollectionChanged();
- }
- }
- #endregion
-
- #endregion
-
- /// <summary>
- /// Check if collection is empty.
- /// </summary>
- /// <value>True if empty</value>
- public abstract bool IsEmpty { get;}
-
- /// <summary>
- /// The number of items in this collection.
- /// </summary>
- /// <value></value>
- public abstract int Count { get;}
-
- /// <summary>
- /// The value is symbolic indicating the type of asymptotic complexity
- /// in terms of the size of this collection (worst-case or amortized as
- /// relevant).
- /// </summary>
- /// <value>A characterization of the speed of the
- /// <code>Count</code> property in this collection.</value>
- public abstract Speed CountSpeed { get; }
-
- /// <summary>
- /// Copy the items of this collection to part of an array.
- /// </summary>
- /// <exception cref="ArgumentOutOfRangeException"> if <code>index</code>
- /// is not a valid index
- /// into the array (i.e. negative or greater than the size of the array)
- /// or the array does not have room for the items.</exception>
- /// <param name="array">The array to copy to.</param>
- /// <param name="index">The starting index.</param>
- [Tested]
- public virtual void CopyTo(T[] array, int index)
- {
- if (index < 0 || index + Count > array.Length)
- throw new ArgumentOutOfRangeException();
-
- foreach (T item in this) array[index++] = item;
- }
-
- /// <summary>
- /// Create an array with the items of this collection (in the same order as an
- /// enumerator would output them).
- /// </summary>
- /// <returns>The array</returns>
- //[Tested]
- public virtual T[] ToArray()
- {
- T[] res = new T[Count];
- int i = 0;
-
- foreach (T item in this) res[i++] = item;
-
- return res;
- }
-
- /// <summary>
- /// Apply an single argument action, <see cref="T:C5.Act`1"/> to this enumerable
- /// </summary>
- /// <param name="action">The action delegate</param>
- [Tested]
- public virtual void Apply(Act<T> action)
- {
- foreach (T item in this)
- action(item);
- }
-
-
- /// <summary>
- /// Check if there exists an item that satisfies a
- /// specific predicate in this collection.
- /// </summary>
- /// <param name="predicate">A delegate
- /// (<see cref="T:C5.Fun`2"/> with <code>R = bool</code>)
- /// defining the predicate</param>
- /// <returns>True if such an item exists</returns>
- [Tested]
- public virtual bool Exists(Fun<T, bool> predicate)
- {
- foreach (T item in this)
- if (predicate(item))
- return true;
-
- return false;
- }
-
- /// <summary>
- /// Check if there exists an item that satisfies a
- /// specific predicate in this collection and return the first one in enumeration order.
- /// </summary>
- /// <param name="predicate">A delegate
- /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param>
- /// <param name="item"></param>
- /// <returns>True is such an item exists</returns>
- public virtual bool Find(Fun<T, bool> predicate, out T item)
- {
- foreach (T jtem in this)
- if (predicate(jtem))
- {
- item = jtem;
- return true;
- }
- item = default(T);
- return false;
- }
-
- /// <summary>
- /// Check if all items in this collection satisfies a specific predicate.
- /// </summary>
- /// <param name="predicate">A delegate
- /// (<see cref="T:C5.Fun`2"/> with <code>R = bool</code>)
- /// defining the predicate</param>
- /// <returns>True if all items satisfies the predicate</returns>
- [Tested]
- public virtual bool All(Fun<T, bool> predicate)
- {
- foreach (T item in this)
- if (!predicate(item))
- return false;
-
- return true;
- }
-
- /// <summary>
- /// Create an enumerable, enumerating the items of this collection that satisfies
- /// a certain condition.
- /// </summary>
- /// <param name="predicate">A delegate
- /// (<see cref="T:C5.Fun`2"/> with <code>R = bool</code>)
- /// defining the predicate</param>
- /// <returns>The filtered enumerable</returns>
- public virtual SCG.IEnumerable<T> Filter(Fun<T, bool> predicate)
- {
- foreach (T item in this)
- if (predicate(item))
- yield return item;
- }
-
- /// <summary>
- /// Choose some item of this collection.
- /// </summary>
- /// <exception cref="NoSuchItemException">if collection is empty.</exception>
- /// <returns></returns>
- public abstract T Choose();
-
-
- /// <summary>
- /// Create an enumerator for this collection.
- /// </summary>
- /// <returns>The enumerator</returns>
- public override abstract SCG.IEnumerator<T> GetEnumerator();
-
- #region IShowable Members
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="stringbuilder"></param>
- /// <param name="rest"></param>
- /// <param name="formatProvider"></param>
- /// <returns></returns>
- public virtual bool Show(System.Text.StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider)
- {
- return Showing.ShowCollectionValue<T>(this, stringbuilder, ref rest, formatProvider);
- }
- #endregion
-
- #region IFormattable Members
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="format"></param>
- /// <param name="formatProvider"></param>
- /// <returns></returns>
- public virtual string ToString(string format, IFormatProvider formatProvider)
- {
- return Showing.ShowString(this, format, formatProvider);
- }
-
- #endregion
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public override string ToString()
- {
- return ToString(null, null);
- }
-
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <typeparam name="T"></typeparam>
- public abstract class DirectedCollectionValueBase<T> : CollectionValueBase<T>, IDirectedCollectionValue<T>
- {
- /// <summary>
- /// <code>Forwards</code> if same, else <code>Backwards</code>
- /// </summary>
- /// <value>The enumeration direction relative to the original collection.</value>
- public virtual EnumerationDirection Direction { [Tested]get { return EnumerationDirection.Forwards; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public abstract IDirectedCollectionValue<T> Backwards();
-
- IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return this.Backwards(); }
-
- /// <summary>
- /// Check if there exists an item that satisfies a
- /// specific predicate in this collection and return the first one in enumeration order.
- /// </summary>
- /// <param name="predicate">A delegate
- /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param>
- /// <param name="item"></param>
- /// <returns>True is such an item exists</returns>
- public virtual bool FindLast(Fun<T, bool> predicate, out T item)
- {
- foreach (T jtem in Backwards())
- if (predicate(jtem))
- {
- item = jtem;
- return true;
- }
- item = default(T);
- return false;
- }
- }
-
- /// <summary>
- /// Base class (abstract) for ICollection implementations.
- /// </summary>
- [Serializable]
- public abstract class CollectionBase<T> : CollectionValueBase<T>
- {
- #region Fields
-
- /// <summary>
- /// The underlying field of the ReadOnly property
- /// </summary>
- protected bool isReadOnlyBase = false;
-
- /// <summary>
- /// The current stamp value
- /// </summary>
- protected int stamp;
-
- /// <summary>
- /// The number of items in the collection
- /// </summary>
- protected int size;
-
- /// <summary>
- /// The item equalityComparer of the collection
- /// </summary>
- protected readonly SCG.IEqualityComparer<T> itemequalityComparer;
-
- int iUnSequencedHashCode, iUnSequencedHashCodeStamp = -1;
-
- #endregion
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="itemequalityComparer"></param>
- protected CollectionBase(SCG.IEqualityComparer<T> itemequalityComparer)
- {
- if (itemequalityComparer == null)
- throw new NullReferenceException("Item EqualityComparer cannot be null.");
- this.itemequalityComparer = itemequalityComparer;
- }
-
- #region Util
-
- /// <summary>
- /// Utility method for range checking.
- /// </summary>
- /// <exception cref="ArgumentOutOfRangeException"> if the start or count is negative or
- /// if the range does not fit within collection size.</exception>
- /// <param name="start">start of range</param>
- /// <param name="count">size of range</param>
- [Tested]
- protected void checkRange(int start, int count)
- {
- if (start < 0 || count < 0 || start + count > size)
- throw new ArgumentOutOfRangeException();
- }
-
-
- /// <summary>
- /// Compute the unsequenced hash code of a collection
- /// </summary>
- /// <param name="items">The collection to compute hash code for</param>
- /// <param name="itemequalityComparer">The item equalityComparer</param>
- /// <returns>The hash code</returns>
- [Tested]
- public static int ComputeHashCode(ICollectionValue<T> items, SCG.IEqualityComparer<T> itemequalityComparer)
- {
- int h = 0;
-
-#if IMPROVED_COLLECTION_HASHFUNCTION
- //But still heuristic:
- //Note: the three odd factors should really be random,
- //but there will be a problem with serialization/deserialization!
- //Two products is too few
- foreach (T item in items)
- {
- uint h1 = (uint)itemequalityComparer.GetHashCode(item);
-
- h += (int)((h1 * 1529784657 + 1) ^ (h1 * 2912831877) ^ (h1 * 1118771817 + 2));
- }
-
- return h;
- /*
- The pairs (-1657792980, -1570288808) and (1862883298, -272461342) gives the same
- unsequenced hashcode with this hashfunction. The pair was found with code like
-
- HashDictionary<int, int[]> set = new HashDictionary<int, int[]>();
- Random rnd = new C5Random(12345);
- while (true)
- {
- int[] a = new int[2];
- a[0] = rnd.Next(); a[1] = rnd.Next();
- int h = unsequencedhashcode(a);
- int[] b = a;
- if (set.FindOrAdd(h, ref b))
- {
- Console.WriteLine("Code {5}, Pair ({1},{2}) number {0} matched other pair ({3},{4})", set.Count, a[0], a[1], b[0], b[1], h);
- }
- }
- */
-#else
- foreach (T item in items)
- h ^= itemequalityComparer.GetHashCode(item);
-
- return (items.Count << 16) + h;
-#endif
- }
-
- static Type isortedtype = typeof(ISorted<T>);
-
- /// <summary>
- /// Examine if collection1 and collection2 are equal as unsequenced collections
- /// using the specified item equalityComparer (assumed compatible with the two collections).
- /// </summary>
- /// <param name="collection1">The first collection</param>
- /// <param name="collection2">The second collection</param>
- /// <param name="itemequalityComparer">The item equalityComparer to use for comparison</param>
- /// <returns>True if equal</returns>
- [Tested]
- public static bool StaticEquals(ICollection<T> collection1, ICollection<T> collection2, SCG.IEqualityComparer<T> itemequalityComparer)
- {
- if (object.ReferenceEquals(collection1, collection2))
- return true;
-
- // bug20070227:
- if (collection1 == null || collection2 == null)
- return false;
-
- if (collection1.Count != collection2.Count)
- return false;
-
- //This way we might run through both enumerations twice, but
- //probably not (if the hash codes are good)
- //TODO: check equal equalityComparers, at least here!
- if (collection1.GetUnsequencedHashCode() != collection2.GetUnsequencedHashCode())
- return false;
-
- //TODO: move this to the sorted implementation classes?
- //Really depends on speed of InstanceOfType: we could save a cast
- {
- ISorted<T> stit, stat;
- if ((stit = collection1 as ISorted<T>) != null && (stat = collection2 as ISorted<T>) != null && stit.Comparer == stat.Comparer)
- {
- using (SCG.IEnumerator<T> dat = collection2.GetEnumerator(), dit = collection1.GetEnumerator())
- {
- while (dit.MoveNext())
- {
- dat.MoveNext();
- if (!itemequalityComparer.Equals(dit.Current, dat.Current))
- return false;
- }
- return true;
- }
- }
- }
-
- if (!collection1.AllowsDuplicates && (collection2.AllowsDuplicates || collection2.ContainsSpeed >= collection1.ContainsSpeed))
- {
- foreach (T x in collection1) if (!collection2.Contains(x)) return false;
- }
- else if (!collection2.AllowsDuplicates)
- {
- foreach (T x in collection2) if (!collection1.Contains(x)) return false;
- }
- // Now tit.AllowsDuplicates && tat.AllowsDuplicates
- else if (collection1.DuplicatesByCounting && collection2.DuplicatesByCounting)
- {
- foreach (T item in collection2) if (collection1.ContainsCount(item) != collection2.ContainsCount(item)) return false;
- }
- else
- {
- //To avoid an O(n^2) algorithm, we make an aux hashtable to hold the count of items
- HashDictionary<T, int> dict = new HashDictionary<T, int>();
- foreach (T item in collection2)
- {
- int count = 1;
- if (dict.FindOrAdd(item, ref count))
- dict[item] = count + 1;
- }
- foreach (T item in collection1)
- {
- int count;
- if (dict.Find(item, out count) && count > 0)
- dict[item] = count - 1;
- else
- return false;
- }
- return true;
- }
-
- return true;
- }
-
-
- /// <summary>
- /// Get the unsequenced collection hash code of this collection: from the cached
- /// value if present and up to date, else (re)compute.
- /// </summary>
- /// <returns>The hash code</returns>
- public virtual int GetUnsequencedHashCode()
- {
- if (iUnSequencedHashCodeStamp == stamp)
- return iUnSequencedHashCode;
-
- iUnSequencedHashCode = ComputeHashCode(this, itemequalityComparer);
- iUnSequencedHashCodeStamp = stamp;
- return iUnSequencedHashCode;
- }
-
-
- /// <summary>
- /// Check if the contents of otherCollection is equal to the contents of this
- /// in the unsequenced sense. Uses the item equality comparer of this collection
- /// </summary>
- /// <param name="otherCollection">The collection to compare to.</param>
- /// <returns>True if equal</returns>
- public virtual bool UnsequencedEquals(ICollection<T> otherCollection)
- {
- return otherCollection != null && StaticEquals((ICollection<T>)this, otherCollection, itemequalityComparer);
- }
-
-
- /// <summary>
- /// Check if the collection has been modified since a specified time, expressed as a stamp value.
- /// </summary>
- /// <exception cref="CollectionModifiedException"> if this collection has been updated
- /// since a target time</exception>
- /// <param name="thestamp">The stamp identifying the target time</param>
- protected virtual void modifycheck(int thestamp)
- {
- if (this.stamp != thestamp)
- throw new CollectionModifiedException();
- }
-
-
- /// <summary>
- /// Check if it is valid to perform update operations, and if so increment stamp.
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException">If colection is read-only</exception>
- protected virtual void updatecheck()
- {
- if (isReadOnlyBase)
- throw new ReadOnlyCollectionException();
-
- stamp++;
- }
-
- #endregion
-
- #region ICollection<T> members
-
- /// <summary>
- ///
- /// </summary>
- /// <value>True if this collection is read only</value>
- [Tested]
- public virtual bool IsReadOnly { [Tested]get { return isReadOnlyBase; } }
-
- #endregion
-
- #region ICollectionValue<T> members
- /// <summary>
- ///
- /// </summary>
- /// <value>The size of this collection</value>
- [Tested]
- public override int Count { [Tested]get { return size; } }
-
- /// <summary>
- /// The value is symbolic indicating the type of asymptotic complexity
- /// in terms of the size of this collection (worst-case or amortized as
- /// relevant).
- /// </summary>
- /// <value>A characterization of the speed of the
- /// <code>Count</code> property in this collection.</value>
- public override Speed CountSpeed { get { return Speed.Constant; } }
-
-
- #endregion
-
- #region IExtensible<T> members
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public virtual SCG.IEqualityComparer<T> EqualityComparer { get { return itemequalityComparer; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <value>True if this collection is empty</value>
- [Tested]
- public override bool IsEmpty { [Tested]get { return size == 0; } }
-
- #endregion
-
- #region IEnumerable<T> Members
- /// <summary>
- /// Create an enumerator for this collection.
- /// </summary>
- /// <returns>The enumerator</returns>
- public override abstract SCG.IEnumerator<T> GetEnumerator();
- #endregion
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <typeparam name="T"></typeparam>
- [Serializable]
- public abstract class DirectedCollectionBase<T> : CollectionBase<T>, IDirectedCollectionValue<T>
- {
- /// <summary>
- ///
- /// </summary>
- /// <param name="itemequalityComparer"></param>
- protected DirectedCollectionBase(SCG.IEqualityComparer<T> itemequalityComparer) : base(itemequalityComparer) { }
- /// <summary>
- /// <code>Forwards</code> if same, else <code>Backwards</code>
- /// </summary>
- /// <value>The enumeration direction relative to the original collection.</value>
- public virtual EnumerationDirection Direction { [Tested]get { return EnumerationDirection.Forwards; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public abstract IDirectedCollectionValue<T> Backwards();
-
- IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return this.Backwards(); }
-
- /// <summary>
- /// Check if there exists an item that satisfies a
- /// specific predicate in this collection and return the first one in enumeration order.
- /// </summary>
- /// <param name="predicate">A delegate
- /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param>
- /// <param name="item"></param>
- /// <returns>True is such an item exists</returns>
- public virtual bool FindLast(Fun<T, bool> predicate, out T item)
- {
- foreach (T jtem in Backwards())
- if (predicate(jtem))
- {
- item = jtem;
- return true;
- }
- item = default(T);
- return false;
- }
- }
-
- /// <summary>
- /// Base class (abstract) for sequenced collection implementations.
- /// </summary>
- [Serializable]
- public abstract class SequencedBase<T> : DirectedCollectionBase<T>, IDirectedCollectionValue<T>
- {
- #region Fields
-
- int iSequencedHashCode, iSequencedHashCodeStamp = -1;
-
- #endregion
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="itemequalityComparer"></param>
- protected SequencedBase(SCG.IEqualityComparer<T> itemequalityComparer) : base(itemequalityComparer) { }
-
- #region Util
-
- //TODO: make random for release
- const int HASHFACTOR = 31;
-
- /// <summary>
- /// Compute the unsequenced hash code of a collection
- /// </summary>
- /// <param name="items">The collection to compute hash code for</param>
- /// <param name="itemequalityComparer">The item equalityComparer</param>
- /// <returns>The hash code</returns>
- [Tested]
- public static int ComputeHashCode(ISequenced<T> items, SCG.IEqualityComparer<T> itemequalityComparer)
- {
- //NOTE: It must be possible to devise a much stronger combined hashcode,
- //but unfortunately, it has to be universal. OR we could use a (strong)
- //family and initialise its parameter randomly at load time of this class!
- //(We would not want to have yet a flag to check for invalidation?!)
- //NBNBNB: the current hashcode has the very bad property that items with hashcode 0
- // is ignored.
- int iIndexedHashCode = 0;
-
- foreach (T item in items)
- iIndexedHashCode = iIndexedHashCode * HASHFACTOR + itemequalityComparer.GetHashCode(item);
-
- return iIndexedHashCode;
- }
-
-
- /// <summary>
- /// Examine if tit and tat are equal as sequenced collections
- /// using the specified item equalityComparer (assumed compatible with the two collections).
- /// </summary>
- /// <param name="collection1">The first collection</param>
- /// <param name="collection2">The second collection</param>
- /// <param name="itemequalityComparer">The item equalityComparer to use for comparison</param>
- /// <returns>True if equal</returns>
- [Tested]
- public static bool StaticEquals(ISequenced<T> collection1, ISequenced<T> collection2, SCG.IEqualityComparer<T> itemequalityComparer)
- {
- if (object.ReferenceEquals(collection1, collection2))
- return true;
-
- if (collection1.Count != collection2.Count)
- return false;
-
- //This way we might run through both enumerations twice, but
- //probably not (if the hash codes are good)
- if (collection1.GetSequencedHashCode() != collection2.GetSequencedHashCode())
- return false;
-
- using (SCG.IEnumerator<T> dat = collection2.GetEnumerator(), dit = collection1.GetEnumerator())
- {
- while (dit.MoveNext())
- {
- dat.MoveNext();
- if (!itemequalityComparer.Equals(dit.Current, dat.Current))
- return false;
- }
- }
-
- return true;
- }
-
-
- /// <summary>
- /// Get the sequenced collection hash code of this collection: from the cached
- /// value if present and up to date, else (re)compute.
- /// </summary>
- /// <returns>The hash code</returns>
- public virtual int GetSequencedHashCode()
- {
- if (iSequencedHashCodeStamp == stamp)
- return iSequencedHashCode;
-
- iSequencedHashCode = ComputeHashCode((ISequenced<T>)this, itemequalityComparer);
- iSequencedHashCodeStamp = stamp;
- return iSequencedHashCode;
- }
-
-
- /// <summary>
- /// Check if the contents of that is equal to the contents of this
- /// in the sequenced sense. Using the item equalityComparer of this collection.
- /// </summary>
- /// <param name="otherCollection">The collection to compare to.</param>
- /// <returns>True if equal</returns>
- public virtual bool SequencedEquals(ISequenced<T> otherCollection)
- {
- return StaticEquals((ISequenced<T>)this, otherCollection, itemequalityComparer);
- }
-
-
- #endregion
-
- /// <summary>
- /// Create an enumerator for this collection.
- /// </summary>
- /// <returns>The enumerator</returns>
- public override abstract SCG.IEnumerator<T> GetEnumerator();
-
- /// <summary>
- /// <code>Forwards</code> if same, else <code>Backwards</code>
- /// </summary>
- /// <value>The enumeration direction relative to the original collection.</value>
- [Tested]
- public override EnumerationDirection Direction { [Tested]get { return EnumerationDirection.Forwards; } }
-
- /// <summary>
- /// Check if there exists an item that satisfies a
- /// specific predicate in this collection and return the index of the first one.
- /// </summary>
- /// <param name="predicate">A delegate
- /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param>
- /// <returns>the index, if found, a negative value else</returns>
- public int FindIndex(Fun<T, bool> predicate)
- {
- int index = 0;
- foreach (T item in this)
- {
- if (predicate(item))
- return index;
- index++;
- }
- return -1;
- }
-
- /// <summary>
- /// Check if there exists an item that satisfies a
- /// specific predicate in this collection and return the index of the last one.
- /// </summary>
- /// <param name="predicate">A delegate
- /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param>
- /// <returns>the index, if found, a negative value else</returns>
- public int FindLastIndex(Fun<T, bool> predicate)
- {
- int index = Count - 1;
- foreach (T item in Backwards())
- {
- if (predicate(item))
- return index;
- index--;
- }
- return -1;
- }
-
- }
-
-
- /// <summary>
- /// Base class for collection classes of dynamic array type implementations.
- /// </summary>
- [Serializable]
- public abstract class ArrayBase<T> : SequencedBase<T>
- {
- #region Fields
- /// <summary>
- /// The actual internal array container. Will be extended on demand.
- /// </summary>
- protected T[] array;
-
- /// <summary>
- /// The offset into the internal array container of the first item. The offset is 0 for a
- /// base dynamic array and may be positive for an updatable view into a base dynamic array.
- /// </summary>
- protected int offset;
- #endregion
-
- #region Util
- /// <summary>
- /// Double the size of the internal array.
- /// </summary>
- protected virtual void expand()
- {
- expand(2 * array.Length, size);
- }
-
-
- /// <summary>
- /// Expand the internal array container.
- /// </summary>
- /// <param name="newcapacity">The new size of the internal array -
- /// will be rounded upwards to a power of 2.</param>
- /// <param name="newsize">The (new) size of the (base) collection.</param>
- protected virtual void expand(int newcapacity, int newsize)
- {
- Debug.Assert(newcapacity >= newsize);
-
- int newlength = array.Length;
-
- while (newlength < newcapacity) newlength *= 2;
-
- T[] newarray = new T[newlength];
-
- Array.Copy(array, newarray, newsize);
- array = newarray;
- }
-
-
- /// <summary>
- /// Insert an item at a specific index, moving items to the right
- /// upwards and expanding the array if necessary.
- /// </summary>
- /// <param name="i">The index at which to insert.</param>
- /// <param name="item">The item to insert.</param>
- protected virtual void insert(int i, T item)
- {
- if (size == array.Length)
- expand();
-
- if (i < size)
- Array.Copy(array, i, array, i + 1, size - i);
-
- array[i] = item;
- size++;
- }
-
- #endregion
-
- #region Constructors
-
- /// <summary>
- /// Create an empty ArrayBase object.
- /// </summary>
- /// <param name="capacity">The initial capacity of the internal array container.
- /// Will be rounded upwards to the nearest power of 2 greater than or equal to 8.</param>
- /// <param name="itemequalityComparer">The item equalityComparer to use, primarily for item equality</param>
- protected ArrayBase(int capacity, SCG.IEqualityComparer<T> itemequalityComparer)
- : base(itemequalityComparer)
- {
- int newlength = 8;
- while (newlength < capacity) newlength *= 2;
- array = new T[newlength];
- }
-
- #endregion
-
- #region IIndexed members
-
- /// <summary>
- /// </summary>
- /// <exception cref="ArgumentOutOfRangeException">If the arguments does not describe a
- /// valid range in the indexed collection, cf. <see cref="M:C5.CollectionBase`1.checkRange(System.Int32,System.Int32)"/>.</exception>
- /// <value>The directed collection of items in a specific index interval.</value>
- /// <param name="start">The low index of the interval (inclusive).</param>
- /// <param name="count">The size of the range.</param>
- [Tested]
- public virtual IDirectedCollectionValue<T> this[int start, int count]
- {
- [Tested]
- get
- {
- checkRange(start, count);
- return new Range(this, start, count, true);
- }
- }
-
- #endregion
-
- #region IEditableCollection members
- /// <summary>
- /// Remove all items and reset size of internal array container.
- /// </summary>
- [Tested]
- public virtual void Clear()
- {
- updatecheck();
- array = new T[8];
- size = 0;
- }
-
-
- /// <summary>
- /// Create an array containing (copies) of the items of this collection in enumeration order.
- /// </summary>
- /// <returns>The new array</returns>
- [Tested]
- public override T[] ToArray()
- {
- T[] res = new T[size];
-
- Array.Copy(array, offset, res, 0, size);
- return res;
- }
-
-
- /// <summary>
- /// Perform an internal consistency (invariant) test on the array base.
- /// </summary>
- /// <returns>True if test succeeds.</returns>
- [Tested]
- public virtual bool Check()
- {
- bool retval = true;
-
- if (size > array.Length)
- {
- Console.WriteLine("Bad size ({0}) > array.Length ({1})", size, array.Length);
- return false;
- }
-
- for (int i = 0; i < size; i++)
- {
- if ((object)(array[i]) == null)
- {
- Console.WriteLine("Bad element: null at index {0}", i);
- return false;
- }
- }
-
- return retval;
- }
-
- #endregion
-
- #region IDirectedCollection<T> Members
-
- /// <summary>
- /// Create a directed collection with the same contents as this one, but
- /// opposite enumeration sequence.
- /// </summary>
- /// <returns>The mirrored collection.</returns>
- [Tested]
- public override IDirectedCollectionValue<T> Backwards() { return this[0, size].Backwards(); }
-
- #endregion
-
- /// <summary>
- /// Choose some item of this collection. The result is the last item in the internal array,
- /// making it efficient to remove.
- /// </summary>
- /// <exception cref="NoSuchItemException">if collection is empty.</exception>
- /// <returns></returns>
- public override T Choose() { if (size > 0) return array[size - 1]; throw new NoSuchItemException(); }
-
- #region IEnumerable<T> Members
- /// <summary>
- /// Create an enumerator for this array based collection.
- /// </summary>
- /// <returns>The enumerator</returns>
- [Tested]
- public override SCG.IEnumerator<T> GetEnumerator()
- {
- int thestamp = stamp, theend = size + offset, thestart = offset;
-
- for (int i = thestart; i < theend; i++)
- {
- modifycheck(thestamp);
- yield return array[i];
- }
- }
- #endregion
-
- #region Range nested class
- /// <summary>
- /// A helper class for defining results of interval queries on array based collections.
- /// </summary>
- protected class Range : DirectedCollectionValueBase<T>, IDirectedCollectionValue<T>
- {
- int start, count, delta, stamp;
-
- ArrayBase<T> thebase;
-
-
- internal Range(ArrayBase<T> thebase, int start, int count, bool forwards)
- {
- this.thebase = thebase; stamp = thebase.stamp;
- delta = forwards ? 1 : -1;
- this.start = start + thebase.offset; this.count = count;
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception>
- /// <value>True if this collection is empty.</value>
- public override bool IsEmpty { get { thebase.modifycheck(stamp); return count == 0; } }
-
-
- /// <summary>
- ///
- /// </summary>
- /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception>
- /// <value>The number of items in the range</value>
- [Tested]
- public override int Count { [Tested]get { thebase.modifycheck(stamp); return count; } }
-
- /// <summary>
- /// The value is symbolic indicating the type of asymptotic complexity
- /// in terms of the size of this collection (worst-case or amortized as
- /// relevant).
- /// </summary>
- /// <value>A characterization of the speed of the
- /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception>
- /// <code>Count</code> property in this collection.</value>
- public override Speed CountSpeed { get { thebase.modifycheck(stamp); return Speed.Constant; } }
-
- /// <summary>
- /// Choose some item of this collection.
- /// </summary>
- /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception>
- /// <exception cref="NoSuchItemException">if range is empty.</exception>
- /// <returns></returns>
- public override T Choose()
- {
- thebase.modifycheck(stamp);
- if (count == 0)
- throw new NoSuchItemException();
- return thebase.array[start];
- }
-
-
- /// <summary>
- /// Create an enumerator for this range of an array based collection.
- /// </summary>
- /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception>
- /// <returns>The enumerator</returns>
- [Tested]
- public override SCG.IEnumerator<T> GetEnumerator()
- {
- for (int i = 0; i < count; i++)
- {
- thebase.modifycheck(stamp);
- yield return thebase.array[start + delta * i];
- }
- }
-
-
- /// <summary>
- /// Create a araay collection range with the same contents as this one, but
- /// opposite enumeration sequence.
- /// </summary>
- /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception>
- /// <returns>The mirrored collection.</returns>
- [Tested]
- public override IDirectedCollectionValue<T> Backwards()
- {
- thebase.modifycheck(stamp);
-
- Range res = (Range)MemberwiseClone();
-
- res.delta = -delta;
- res.start = start + (count - 1) * delta;
- return res;
- }
-
-
- IDirectedEnumerable<T> C5.IDirectedEnumerable<T>.Backwards()
- {
- return Backwards();
- }
-
-
- /// <summary>
- /// <code>Forwards</code> if same, else <code>Backwards</code>
- /// </summary>
- /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception>
- /// <value>The enumeration direction relative to the original collection.</value>
- [Tested]
- public override EnumerationDirection Direction
- {
- [Tested]
- get
- {
- thebase.modifycheck(stamp);
- return delta > 0 ? EnumerationDirection.Forwards : EnumerationDirection.Backwards;
- }
- }
- }
- #endregion
- }
-}
+/* + Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#define IMPROVED_COLLECTION_HASHFUNCTION + +using System; +using System.Diagnostics; +using SCG = System.Collections.Generic; +namespace C5 +{ + /// <summary> + /// A base class for implementing an IEnumerable<T> + /// </summary> + [Serializable] + public abstract class EnumerableBase<T> : SCG.IEnumerable<T> + { + /// <summary> + /// Create an enumerator for this collection. + /// </summary> + /// <returns>The enumerator</returns> + public abstract SCG.IEnumerator<T> GetEnumerator(); + + /// <summary> + /// Count the number of items in an enumerable by enumeration + /// </summary> + /// <param name="items">The enumerable to count</param> + /// <returns>The size of the enumerable</returns> + [Tested] + protected static int countItems(SCG.IEnumerable<T> items) + { + ICollectionValue<T> jtems = items as ICollectionValue<T>; + + if (jtems != null) + return jtems.Count; + + int count = 0; + + using (SCG.IEnumerator<T> e = items.GetEnumerator()) + while (e.MoveNext()) count++; + + return count; + } + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + } + + + /// <summary> + /// Base class for classes implementing ICollectionValue[T] + /// </summary> + [Serializable] + public abstract class CollectionValueBase<T> : EnumerableBase<T>, ICollectionValue<T>, IShowable + { + #region Event handling + EventBlock<T> eventBlock; + /// <summary> + /// + /// </summary> + /// <value></value> + public virtual EventTypeEnum ListenableEvents { get { return 0; } } + + /// <summary> + /// A flag bitmap of the events currently subscribed to by this collection. + /// </summary> + /// <value></value> + public virtual EventTypeEnum ActiveEvents { get { return eventBlock == null ? 0 : eventBlock.events; } } + + private void checkWillListen(EventTypeEnum eventType) + { + if ((ListenableEvents & eventType) == 0) + throw new UnlistenableEventException(); + } + + /// <summary> + /// The change event. Will be raised for every change operation on the collection. + /// </summary> + public virtual event CollectionChangedHandler<T> CollectionChanged + { + add { checkWillListen(EventTypeEnum.Changed); (eventBlock ?? (eventBlock = new EventBlock<T>())).CollectionChanged += value; } + remove + { + checkWillListen(EventTypeEnum.Changed); + if (eventBlock != null) + { + eventBlock.CollectionChanged -= value; + if (eventBlock.events == 0) eventBlock = null; + } + } + } + /// <summary> + /// Fire the CollectionChanged event + /// </summary> + protected virtual void raiseCollectionChanged() + { if (eventBlock != null) eventBlock.raiseCollectionChanged(this); } + + /// <summary> + /// The clear event. Will be raised for every Clear operation on the collection. + /// </summary> + public virtual event CollectionClearedHandler<T> CollectionCleared + { + add { checkWillListen(EventTypeEnum.Cleared); (eventBlock ?? (eventBlock = new EventBlock<T>())).CollectionCleared += value; } + remove + { + checkWillListen(EventTypeEnum.Cleared); + if (eventBlock != null) + { + eventBlock.CollectionCleared -= value; + if (eventBlock.events == 0) eventBlock = null; + } + } + } + /// <summary> + /// Fire the CollectionCleared event + /// </summary> + protected virtual void raiseCollectionCleared(bool full, int count) + { if (eventBlock != null) eventBlock.raiseCollectionCleared(this, full, count); } + + /// <summary> + /// Fire the CollectionCleared event + /// </summary> + protected virtual void raiseCollectionCleared(bool full, int count, int? offset) + { if (eventBlock != null) eventBlock.raiseCollectionCleared(this, full, count, offset); } + + /// <summary> + /// The item added event. Will be raised for every individual addition to the collection. + /// </summary> + public virtual event ItemsAddedHandler<T> ItemsAdded + { + add { checkWillListen(EventTypeEnum.Added); (eventBlock ?? (eventBlock = new EventBlock<T>())).ItemsAdded += value; } + remove + { + checkWillListen(EventTypeEnum.Added); + if (eventBlock != null) + { + eventBlock.ItemsAdded -= value; + if (eventBlock.events == 0) eventBlock = null; + } + } + } + /// <summary> + /// Fire the ItemsAdded event + /// </summary> + /// <param name="item">The item that was added</param> + /// <param name="count"></param> + protected virtual void raiseItemsAdded(T item, int count) + { if (eventBlock != null) eventBlock.raiseItemsAdded(this, item, count); } + + /// <summary> + /// The item removed event. Will be raised for every individual removal from the collection. + /// </summary> + public virtual event ItemsRemovedHandler<T> ItemsRemoved + { + add { checkWillListen(EventTypeEnum.Removed); (eventBlock ?? (eventBlock = new EventBlock<T>())).ItemsRemoved += value; } + remove + { + checkWillListen(EventTypeEnum.Removed); + if (eventBlock != null) + { + eventBlock.ItemsRemoved -= value; + if (eventBlock.events == 0) eventBlock = null; + } + } + } + /// <summary> + /// Fire the ItemsRemoved event + /// </summary> + /// <param name="item">The item that was removed</param> + /// <param name="count"></param> + protected virtual void raiseItemsRemoved(T item, int count) + { if (eventBlock != null) eventBlock.raiseItemsRemoved(this, item, count); } + + /// <summary> + /// The item added event. Will be raised for every individual addition to the collection. + /// </summary> + public virtual event ItemInsertedHandler<T> ItemInserted + { + add { checkWillListen(EventTypeEnum.Inserted); (eventBlock ?? (eventBlock = new EventBlock<T>())).ItemInserted += value; } + remove + { + checkWillListen(EventTypeEnum.Inserted); + if (eventBlock != null) + { + eventBlock.ItemInserted -= value; + if (eventBlock.events == 0) eventBlock = null; + } + } + } + /// <summary> + /// Fire the ItemInserted event + /// </summary> + /// <param name="item">The item that was added</param> + /// <param name="index"></param> + protected virtual void raiseItemInserted(T item, int index) + { if (eventBlock != null) eventBlock.raiseItemInserted(this, item, index); } + + /// <summary> + /// The item removed event. Will be raised for every individual removal from the collection. + /// </summary> + public virtual event ItemRemovedAtHandler<T> ItemRemovedAt + { + add { checkWillListen(EventTypeEnum.RemovedAt); (eventBlock ?? (eventBlock = new EventBlock<T>())).ItemRemovedAt += value; } + remove + { + checkWillListen(EventTypeEnum.RemovedAt); + if (eventBlock != null) + { + eventBlock.ItemRemovedAt -= value; + if (eventBlock.events == 0) eventBlock = null; + } + } + } + /// <summary> + /// Fire the ItemRemovedAt event + /// </summary> + /// <param name="item">The item that was removed</param> + /// <param name="index"></param> + protected virtual void raiseItemRemovedAt(T item, int index) + { if (eventBlock != null) eventBlock.raiseItemRemovedAt(this, item, index); } + + #region Event support for IList + /// <summary> + /// + /// </summary> + /// <param name="index"></param> + /// <param name="value"></param> + /// <param name="item"></param> + protected virtual void raiseForSetThis(int index, T value, T item) + { + if (ActiveEvents != 0) + { + raiseItemsRemoved(item, 1); + raiseItemRemovedAt(item, index); + raiseItemsAdded(value, 1); + raiseItemInserted(value, index); + raiseCollectionChanged(); + } + } + /// <summary> + /// + /// </summary> + /// <param name="i"></param> + /// <param name="item"></param> + protected virtual void raiseForInsert(int i, T item) + { + if (ActiveEvents != 0) + { + raiseItemInserted(item, i); + raiseItemsAdded(item, 1); + raiseCollectionChanged(); + } + } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + protected void raiseForRemove(T item) + { + if (ActiveEvents != 0) + { + raiseItemsRemoved(item, 1); + raiseCollectionChanged(); + } + } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <param name="count"></param> + protected void raiseForRemove(T item, int count) + { + if (ActiveEvents != 0) + { + raiseItemsRemoved(item, count); + raiseCollectionChanged(); + } + } + + /// <summary> + /// + /// </summary> + /// <param name="index"></param> + /// <param name="item"></param> + protected void raiseForRemoveAt(int index, T item) + { + if (ActiveEvents != 0) + { + raiseItemRemovedAt(item, index); + raiseItemsRemoved(item, 1); + raiseCollectionChanged(); + } + } + + #endregion + + #region Event Support for ICollection + /// <summary> + /// + /// </summary> + /// <param name="newitem"></param> + /// <param name="olditem"></param> + protected virtual void raiseForUpdate(T newitem, T olditem) + { + if (ActiveEvents != 0) + { + raiseItemsRemoved(olditem, 1); + raiseItemsAdded(newitem, 1); + raiseCollectionChanged(); + } + } + /// <summary> + /// + /// </summary> + /// <param name="newitem"></param> + /// <param name="olditem"></param> + /// <param name="count"></param> + protected virtual void raiseForUpdate(T newitem, T olditem, int count) + { + if (ActiveEvents != 0) + { + raiseItemsRemoved(olditem, count); + raiseItemsAdded(newitem, count); + raiseCollectionChanged(); + } + } + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + protected virtual void raiseForAdd(T item) + { + if (ActiveEvents != 0) + { + raiseItemsAdded(item, 1); + raiseCollectionChanged(); + } + } + /// <summary> + /// + /// </summary> + /// <param name="wasRemoved"></param> + protected virtual void raiseForRemoveAll(ICollectionValue<T> wasRemoved) + { + if ((ActiveEvents & EventTypeEnum.Removed) != 0) + foreach (T item in wasRemoved) + raiseItemsRemoved(item, 1); + if (wasRemoved != null && wasRemoved.Count > 0) + raiseCollectionChanged(); + } + + /// <summary> + /// + /// </summary> + protected class RaiseForRemoveAllHandler + { + CollectionValueBase<T> collection; + CircularQueue<T> wasRemoved; + bool wasChanged = false; + + /// <summary> + /// + /// </summary> + /// <param name="collection"></param> + public RaiseForRemoveAllHandler(CollectionValueBase<T> collection) + { + this.collection = collection; + mustFireRemoved = (collection.ActiveEvents & EventTypeEnum.Removed) != 0; + MustFire = (collection.ActiveEvents & (EventTypeEnum.Removed | EventTypeEnum.Changed)) != 0; + } + + bool mustFireRemoved; + /// <summary> + /// + /// </summary> + public readonly bool MustFire; + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + public void Remove(T item) + { + if (mustFireRemoved) + { + if (wasRemoved == null) + wasRemoved = new CircularQueue<T>(); + wasRemoved.Enqueue(item); + } + if (!wasChanged) + wasChanged = true; + } + + /// <summary> + /// + /// </summary> + public void Raise() + { + if (wasRemoved != null) + foreach (T item in wasRemoved) + collection.raiseItemsRemoved(item, 1); + if (wasChanged) + collection.raiseCollectionChanged(); + } + } + #endregion + + #endregion + + /// <summary> + /// Check if collection is empty. + /// </summary> + /// <value>True if empty</value> + public abstract bool IsEmpty { get;} + + /// <summary> + /// The number of items in this collection. + /// </summary> + /// <value></value> + public abstract int Count { get;} + + /// <summary> + /// The value is symbolic indicating the type of asymptotic complexity + /// in terms of the size of this collection (worst-case or amortized as + /// relevant). + /// </summary> + /// <value>A characterization of the speed of the + /// <code>Count</code> property in this collection.</value> + public abstract Speed CountSpeed { get; } + + /// <summary> + /// Copy the items of this collection to part of an array. + /// </summary> + /// <exception cref="ArgumentOutOfRangeException"> if <code>index</code> + /// is not a valid index + /// into the array (i.e. negative or greater than the size of the array) + /// or the array does not have room for the items.</exception> + /// <param name="array">The array to copy to.</param> + /// <param name="index">The starting index.</param> + [Tested] + public virtual void CopyTo(T[] array, int index) + { + if (index < 0 || index + Count > array.Length) + throw new ArgumentOutOfRangeException(); + + foreach (T item in this) array[index++] = item; + } + + /// <summary> + /// Create an array with the items of this collection (in the same order as an + /// enumerator would output them). + /// </summary> + /// <returns>The array</returns> + //[Tested] + public virtual T[] ToArray() + { + T[] res = new T[Count]; + int i = 0; + + foreach (T item in this) res[i++] = item; + + return res; + } + + /// <summary> + /// Apply an single argument action, <see cref="T:C5.Act`1"/> to this enumerable + /// </summary> + /// <param name="action">The action delegate</param> + [Tested] + public virtual void Apply(Act<T> action) + { + foreach (T item in this) + action(item); + } + + + /// <summary> + /// Check if there exists an item that satisfies a + /// specific predicate in this collection. + /// </summary> + /// <param name="predicate">A delegate + /// (<see cref="T:C5.Fun`2"/> with <code>R = bool</code>) + /// defining the predicate</param> + /// <returns>True if such an item exists</returns> + [Tested] + public virtual bool Exists(Fun<T, bool> predicate) + { + foreach (T item in this) + if (predicate(item)) + return true; + + return false; + } + + /// <summary> + /// Check if there exists an item that satisfies a + /// specific predicate in this collection and return the first one in enumeration order. + /// </summary> + /// <param name="predicate">A delegate + /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param> + /// <param name="item"></param> + /// <returns>True is such an item exists</returns> + public virtual bool Find(Fun<T, bool> predicate, out T item) + { + foreach (T jtem in this) + if (predicate(jtem)) + { + item = jtem; + return true; + } + item = default(T); + return false; + } + + /// <summary> + /// Check if all items in this collection satisfies a specific predicate. + /// </summary> + /// <param name="predicate">A delegate + /// (<see cref="T:C5.Fun`2"/> with <code>R = bool</code>) + /// defining the predicate</param> + /// <returns>True if all items satisfies the predicate</returns> + [Tested] + public virtual bool All(Fun<T, bool> predicate) + { + foreach (T item in this) + if (!predicate(item)) + return false; + + return true; + } + + /// <summary> + /// Create an enumerable, enumerating the items of this collection that satisfies + /// a certain condition. + /// </summary> + /// <param name="predicate">A delegate + /// (<see cref="T:C5.Fun`2"/> with <code>R = bool</code>) + /// defining the predicate</param> + /// <returns>The filtered enumerable</returns> + public virtual SCG.IEnumerable<T> Filter(Fun<T, bool> predicate) + { + foreach (T item in this) + if (predicate(item)) + yield return item; + } + + /// <summary> + /// Choose some item of this collection. + /// </summary> + /// <exception cref="NoSuchItemException">if collection is empty.</exception> + /// <returns></returns> + public abstract T Choose(); + + + /// <summary> + /// Create an enumerator for this collection. + /// </summary> + /// <returns>The enumerator</returns> + public override abstract SCG.IEnumerator<T> GetEnumerator(); + + #region IShowable Members + + /// <summary> + /// + /// </summary> + /// <param name="stringbuilder"></param> + /// <param name="rest"></param> + /// <param name="formatProvider"></param> + /// <returns></returns> + public virtual bool Show(System.Text.StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider) + { + return Showing.ShowCollectionValue<T>(this, stringbuilder, ref rest, formatProvider); + } + #endregion + + #region IFormattable Members + + /// <summary> + /// + /// </summary> + /// <param name="format"></param> + /// <param name="formatProvider"></param> + /// <returns></returns> + public virtual string ToString(string format, IFormatProvider formatProvider) + { + return Showing.ShowString(this, format, formatProvider); + } + + #endregion + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public override string ToString() + { + return ToString(null, null); + } + + } + + /// <summary> + /// + /// </summary> + /// <typeparam name="T"></typeparam> + public abstract class DirectedCollectionValueBase<T> : CollectionValueBase<T>, IDirectedCollectionValue<T> + { + /// <summary> + /// <code>Forwards</code> if same, else <code>Backwards</code> + /// </summary> + /// <value>The enumeration direction relative to the original collection.</value> + public virtual EnumerationDirection Direction { [Tested]get { return EnumerationDirection.Forwards; } } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public abstract IDirectedCollectionValue<T> Backwards(); + + IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return this.Backwards(); } + + /// <summary> + /// Check if there exists an item that satisfies a + /// specific predicate in this collection and return the first one in enumeration order. + /// </summary> + /// <param name="predicate">A delegate + /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param> + /// <param name="item"></param> + /// <returns>True is such an item exists</returns> + public virtual bool FindLast(Fun<T, bool> predicate, out T item) + { + foreach (T jtem in Backwards()) + if (predicate(jtem)) + { + item = jtem; + return true; + } + item = default(T); + return false; + } + } + + /// <summary> + /// Base class (abstract) for ICollection implementations. + /// </summary> + [Serializable] + public abstract class CollectionBase<T> : CollectionValueBase<T> + { + #region Fields + + /// <summary> + /// The underlying field of the ReadOnly property + /// </summary> + protected bool isReadOnlyBase = false; + + /// <summary> + /// The current stamp value + /// </summary> + protected int stamp; + + /// <summary> + /// The number of items in the collection + /// </summary> + protected int size; + + /// <summary> + /// The item equalityComparer of the collection + /// </summary> + protected readonly SCG.IEqualityComparer<T> itemequalityComparer; + + int iUnSequencedHashCode, iUnSequencedHashCodeStamp = -1; + + #endregion + + /// <summary> + /// + /// </summary> + /// <param name="itemequalityComparer"></param> + protected CollectionBase(SCG.IEqualityComparer<T> itemequalityComparer) + { + if (itemequalityComparer == null) + throw new NullReferenceException("Item EqualityComparer cannot be null."); + this.itemequalityComparer = itemequalityComparer; + } + + #region Util + + /// <summary> + /// Utility method for range checking. + /// </summary> + /// <exception cref="ArgumentOutOfRangeException"> if the start or count is negative or + /// if the range does not fit within collection size.</exception> + /// <param name="start">start of range</param> + /// <param name="count">size of range</param> + [Tested] + protected void checkRange(int start, int count) + { + if (start < 0 || count < 0 || start + count > size) + throw new ArgumentOutOfRangeException(); + } + + + /// <summary> + /// Compute the unsequenced hash code of a collection + /// </summary> + /// <param name="items">The collection to compute hash code for</param> + /// <param name="itemequalityComparer">The item equalityComparer</param> + /// <returns>The hash code</returns> + [Tested] + public static int ComputeHashCode(ICollectionValue<T> items, SCG.IEqualityComparer<T> itemequalityComparer) + { + int h = 0; + +#if IMPROVED_COLLECTION_HASHFUNCTION + //But still heuristic: + //Note: the three odd factors should really be random, + //but there will be a problem with serialization/deserialization! + //Two products is too few + foreach (T item in items) + { + uint h1 = (uint)itemequalityComparer.GetHashCode(item); + + h += (int)((h1 * 1529784657 + 1) ^ (h1 * 2912831877) ^ (h1 * 1118771817 + 2)); + } + + return h; + /* + The pairs (-1657792980, -1570288808) and (1862883298, -272461342) gives the same + unsequenced hashcode with this hashfunction. The pair was found with code like + + HashDictionary<int, int[]> set = new HashDictionary<int, int[]>(); + Random rnd = new C5Random(12345); + while (true) + { + int[] a = new int[2]; + a[0] = rnd.Next(); a[1] = rnd.Next(); + int h = unsequencedhashcode(a); + int[] b = a; + if (set.FindOrAdd(h, ref b)) + { + Console.WriteLine("Code {5}, Pair ({1},{2}) number {0} matched other pair ({3},{4})", set.Count, a[0], a[1], b[0], b[1], h); + } + } + */ +#else + foreach (T item in items) + h ^= itemequalityComparer.GetHashCode(item); + + return (items.Count << 16) + h; +#endif + } + + static Type isortedtype = typeof(ISorted<T>); + + /// <summary> + /// Examine if collection1 and collection2 are equal as unsequenced collections + /// using the specified item equalityComparer (assumed compatible with the two collections). + /// </summary> + /// <param name="collection1">The first collection</param> + /// <param name="collection2">The second collection</param> + /// <param name="itemequalityComparer">The item equalityComparer to use for comparison</param> + /// <returns>True if equal</returns> + [Tested] + public static bool StaticEquals(ICollection<T> collection1, ICollection<T> collection2, SCG.IEqualityComparer<T> itemequalityComparer) + { + if (object.ReferenceEquals(collection1, collection2)) + return true; + + // bug20070227: + if (collection1 == null || collection2 == null) + return false; + + if (collection1.Count != collection2.Count) + return false; + + //This way we might run through both enumerations twice, but + //probably not (if the hash codes are good) + //TODO: check equal equalityComparers, at least here! + if (collection1.GetUnsequencedHashCode() != collection2.GetUnsequencedHashCode()) + return false; + + //TODO: move this to the sorted implementation classes? + //Really depends on speed of InstanceOfType: we could save a cast + { + ISorted<T> stit, stat; + if ((stit = collection1 as ISorted<T>) != null && (stat = collection2 as ISorted<T>) != null && stit.Comparer == stat.Comparer) + { + using (SCG.IEnumerator<T> dat = collection2.GetEnumerator(), dit = collection1.GetEnumerator()) + { + while (dit.MoveNext()) + { + dat.MoveNext(); + if (!itemequalityComparer.Equals(dit.Current, dat.Current)) + return false; + } + return true; + } + } + } + + if (!collection1.AllowsDuplicates && (collection2.AllowsDuplicates || collection2.ContainsSpeed >= collection1.ContainsSpeed)) + { + foreach (T x in collection1) if (!collection2.Contains(x)) return false; + } + else if (!collection2.AllowsDuplicates) + { + foreach (T x in collection2) if (!collection1.Contains(x)) return false; + } + // Now tit.AllowsDuplicates && tat.AllowsDuplicates + else if (collection1.DuplicatesByCounting && collection2.DuplicatesByCounting) + { + foreach (T item in collection2) if (collection1.ContainsCount(item) != collection2.ContainsCount(item)) return false; + } + else + { + //To avoid an O(n^2) algorithm, we make an aux hashtable to hold the count of items + HashDictionary<T, int> dict = new HashDictionary<T, int>(); + foreach (T item in collection2) + { + int count = 1; + if (dict.FindOrAdd(item, ref count)) + dict[item] = count + 1; + } + foreach (T item in collection1) + { + int count; + if (dict.Find(item, out count) && count > 0) + dict[item] = count - 1; + else + return false; + } + return true; + } + + return true; + } + + + /// <summary> + /// Get the unsequenced collection hash code of this collection: from the cached + /// value if present and up to date, else (re)compute. + /// </summary> + /// <returns>The hash code</returns> + public virtual int GetUnsequencedHashCode() + { + if (iUnSequencedHashCodeStamp == stamp) + return iUnSequencedHashCode; + + iUnSequencedHashCode = ComputeHashCode(this, itemequalityComparer); + iUnSequencedHashCodeStamp = stamp; + return iUnSequencedHashCode; + } + + + /// <summary> + /// Check if the contents of otherCollection is equal to the contents of this + /// in the unsequenced sense. Uses the item equality comparer of this collection + /// </summary> + /// <param name="otherCollection">The collection to compare to.</param> + /// <returns>True if equal</returns> + public virtual bool UnsequencedEquals(ICollection<T> otherCollection) + { + return otherCollection != null && StaticEquals((ICollection<T>)this, otherCollection, itemequalityComparer); + } + + + /// <summary> + /// Check if the collection has been modified since a specified time, expressed as a stamp value. + /// </summary> + /// <exception cref="CollectionModifiedException"> if this collection has been updated + /// since a target time</exception> + /// <param name="thestamp">The stamp identifying the target time</param> + protected virtual void modifycheck(int thestamp) + { + if (this.stamp != thestamp) + throw new CollectionModifiedException(); + } + + + /// <summary> + /// Check if it is valid to perform update operations, and if so increment stamp. + /// </summary> + /// <exception cref="ReadOnlyCollectionException">If colection is read-only</exception> + protected virtual void updatecheck() + { + if (isReadOnlyBase) + throw new ReadOnlyCollectionException(); + + stamp++; + } + + #endregion + + #region ICollection<T> members + + /// <summary> + /// + /// </summary> + /// <value>True if this collection is read only</value> + [Tested] + public virtual bool IsReadOnly { [Tested]get { return isReadOnlyBase; } } + + #endregion + + #region ICollectionValue<T> members + /// <summary> + /// + /// </summary> + /// <value>The size of this collection</value> + [Tested] + public override int Count { [Tested]get { return size; } } + + /// <summary> + /// The value is symbolic indicating the type of asymptotic complexity + /// in terms of the size of this collection (worst-case or amortized as + /// relevant). + /// </summary> + /// <value>A characterization of the speed of the + /// <code>Count</code> property in this collection.</value> + public override Speed CountSpeed { get { return Speed.Constant; } } + + + #endregion + + #region IExtensible<T> members + + /// <summary> + /// + /// </summary> + /// <value></value> + public virtual SCG.IEqualityComparer<T> EqualityComparer { get { return itemequalityComparer; } } + + /// <summary> + /// + /// </summary> + /// <value>True if this collection is empty</value> + [Tested] + public override bool IsEmpty { [Tested]get { return size == 0; } } + + #endregion + + #region IEnumerable<T> Members + /// <summary> + /// Create an enumerator for this collection. + /// </summary> + /// <returns>The enumerator</returns> + public override abstract SCG.IEnumerator<T> GetEnumerator(); + #endregion + } + + /// <summary> + /// + /// </summary> + /// <typeparam name="T"></typeparam> + [Serializable] + public abstract class DirectedCollectionBase<T> : CollectionBase<T>, IDirectedCollectionValue<T> + { + /// <summary> + /// + /// </summary> + /// <param name="itemequalityComparer"></param> + protected DirectedCollectionBase(SCG.IEqualityComparer<T> itemequalityComparer) : base(itemequalityComparer) { } + /// <summary> + /// <code>Forwards</code> if same, else <code>Backwards</code> + /// </summary> + /// <value>The enumeration direction relative to the original collection.</value> + public virtual EnumerationDirection Direction { [Tested]get { return EnumerationDirection.Forwards; } } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public abstract IDirectedCollectionValue<T> Backwards(); + + IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return this.Backwards(); } + + /// <summary> + /// Check if there exists an item that satisfies a + /// specific predicate in this collection and return the first one in enumeration order. + /// </summary> + /// <param name="predicate">A delegate + /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param> + /// <param name="item"></param> + /// <returns>True is such an item exists</returns> + public virtual bool FindLast(Fun<T, bool> predicate, out T item) + { + foreach (T jtem in Backwards()) + if (predicate(jtem)) + { + item = jtem; + return true; + } + item = default(T); + return false; + } + } + + /// <summary> + /// Base class (abstract) for sequenced collection implementations. + /// </summary> + [Serializable] + public abstract class SequencedBase<T> : DirectedCollectionBase<T>, IDirectedCollectionValue<T> + { + #region Fields + + int iSequencedHashCode, iSequencedHashCodeStamp = -1; + + #endregion + + /// <summary> + /// + /// </summary> + /// <param name="itemequalityComparer"></param> + protected SequencedBase(SCG.IEqualityComparer<T> itemequalityComparer) : base(itemequalityComparer) { } + + #region Util + + //TODO: make random for release + const int HASHFACTOR = 31; + + /// <summary> + /// Compute the unsequenced hash code of a collection + /// </summary> + /// <param name="items">The collection to compute hash code for</param> + /// <param name="itemequalityComparer">The item equalityComparer</param> + /// <returns>The hash code</returns> + [Tested] + public static int ComputeHashCode(ISequenced<T> items, SCG.IEqualityComparer<T> itemequalityComparer) + { + //NOTE: It must be possible to devise a much stronger combined hashcode, + //but unfortunately, it has to be universal. OR we could use a (strong) + //family and initialise its parameter randomly at load time of this class! + //(We would not want to have yet a flag to check for invalidation?!) + //NBNBNB: the current hashcode has the very bad property that items with hashcode 0 + // is ignored. + int iIndexedHashCode = 0; + + foreach (T item in items) + iIndexedHashCode = iIndexedHashCode * HASHFACTOR + itemequalityComparer.GetHashCode(item); + + return iIndexedHashCode; + } + + + /// <summary> + /// Examine if tit and tat are equal as sequenced collections + /// using the specified item equalityComparer (assumed compatible with the two collections). + /// </summary> + /// <param name="collection1">The first collection</param> + /// <param name="collection2">The second collection</param> + /// <param name="itemequalityComparer">The item equalityComparer to use for comparison</param> + /// <returns>True if equal</returns> + [Tested] + public static bool StaticEquals(ISequenced<T> collection1, ISequenced<T> collection2, SCG.IEqualityComparer<T> itemequalityComparer) + { + if (object.ReferenceEquals(collection1, collection2)) + return true; + + if (collection1.Count != collection2.Count) + return false; + + //This way we might run through both enumerations twice, but + //probably not (if the hash codes are good) + if (collection1.GetSequencedHashCode() != collection2.GetSequencedHashCode()) + return false; + + using (SCG.IEnumerator<T> dat = collection2.GetEnumerator(), dit = collection1.GetEnumerator()) + { + while (dit.MoveNext()) + { + dat.MoveNext(); + if (!itemequalityComparer.Equals(dit.Current, dat.Current)) + return false; + } + } + + return true; + } + + + /// <summary> + /// Get the sequenced collection hash code of this collection: from the cached + /// value if present and up to date, else (re)compute. + /// </summary> + /// <returns>The hash code</returns> + public virtual int GetSequencedHashCode() + { + if (iSequencedHashCodeStamp == stamp) + return iSequencedHashCode; + + iSequencedHashCode = ComputeHashCode((ISequenced<T>)this, itemequalityComparer); + iSequencedHashCodeStamp = stamp; + return iSequencedHashCode; + } + + + /// <summary> + /// Check if the contents of that is equal to the contents of this + /// in the sequenced sense. Using the item equalityComparer of this collection. + /// </summary> + /// <param name="otherCollection">The collection to compare to.</param> + /// <returns>True if equal</returns> + public virtual bool SequencedEquals(ISequenced<T> otherCollection) + { + return StaticEquals((ISequenced<T>)this, otherCollection, itemequalityComparer); + } + + + #endregion + + /// <summary> + /// Create an enumerator for this collection. + /// </summary> + /// <returns>The enumerator</returns> + public override abstract SCG.IEnumerator<T> GetEnumerator(); + + /// <summary> + /// <code>Forwards</code> if same, else <code>Backwards</code> + /// </summary> + /// <value>The enumeration direction relative to the original collection.</value> + [Tested] + public override EnumerationDirection Direction { [Tested]get { return EnumerationDirection.Forwards; } } + + /// <summary> + /// Check if there exists an item that satisfies a + /// specific predicate in this collection and return the index of the first one. + /// </summary> + /// <param name="predicate">A delegate + /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param> + /// <returns>the index, if found, a negative value else</returns> + public int FindIndex(Fun<T, bool> predicate) + { + int index = 0; + foreach (T item in this) + { + if (predicate(item)) + return index; + index++; + } + return -1; + } + + /// <summary> + /// Check if there exists an item that satisfies a + /// specific predicate in this collection and return the index of the last one. + /// </summary> + /// <param name="predicate">A delegate + /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param> + /// <returns>the index, if found, a negative value else</returns> + public int FindLastIndex(Fun<T, bool> predicate) + { + int index = Count - 1; + foreach (T item in Backwards()) + { + if (predicate(item)) + return index; + index--; + } + return -1; + } + + } + + + /// <summary> + /// Base class for collection classes of dynamic array type implementations. + /// </summary> + [Serializable] + public abstract class ArrayBase<T> : SequencedBase<T> + { + #region Fields + /// <summary> + /// The actual internal array container. Will be extended on demand. + /// </summary> + protected T[] array; + + /// <summary> + /// The offset into the internal array container of the first item. The offset is 0 for a + /// base dynamic array and may be positive for an updatable view into a base dynamic array. + /// </summary> + protected int offset; + #endregion + + #region Util + /// <summary> + /// Double the size of the internal array. + /// </summary> + protected virtual void expand() + { + expand(2 * array.Length, size); + } + + + /// <summary> + /// Expand the internal array container. + /// </summary> + /// <param name="newcapacity">The new size of the internal array - + /// will be rounded upwards to a power of 2.</param> + /// <param name="newsize">The (new) size of the (base) collection.</param> + protected virtual void expand(int newcapacity, int newsize) + { + Debug.Assert(newcapacity >= newsize); + + int newlength = array.Length; + + while (newlength < newcapacity) newlength *= 2; + + T[] newarray = new T[newlength]; + + Array.Copy(array, newarray, newsize); + array = newarray; + } + + + /// <summary> + /// Insert an item at a specific index, moving items to the right + /// upwards and expanding the array if necessary. + /// </summary> + /// <param name="i">The index at which to insert.</param> + /// <param name="item">The item to insert.</param> + protected virtual void insert(int i, T item) + { + if (size == array.Length) + expand(); + + if (i < size) + Array.Copy(array, i, array, i + 1, size - i); + + array[i] = item; + size++; + } + + #endregion + + #region Constructors + + /// <summary> + /// Create an empty ArrayBase object. + /// </summary> + /// <param name="capacity">The initial capacity of the internal array container. + /// Will be rounded upwards to the nearest power of 2 greater than or equal to 8.</param> + /// <param name="itemequalityComparer">The item equalityComparer to use, primarily for item equality</param> + protected ArrayBase(int capacity, SCG.IEqualityComparer<T> itemequalityComparer) + : base(itemequalityComparer) + { + int newlength = 8; + while (newlength < capacity) newlength *= 2; + array = new T[newlength]; + } + + #endregion + + #region IIndexed members + + /// <summary> + /// </summary> + /// <exception cref="ArgumentOutOfRangeException">If the arguments does not describe a + /// valid range in the indexed collection, cf. <see cref="M:C5.CollectionBase`1.checkRange(System.Int32,System.Int32)"/>.</exception> + /// <value>The directed collection of items in a specific index interval.</value> + /// <param name="start">The low index of the interval (inclusive).</param> + /// <param name="count">The size of the range.</param> + [Tested] + public virtual IDirectedCollectionValue<T> this[int start, int count] + { + [Tested] + get + { + checkRange(start, count); + return new Range(this, start, count, true); + } + } + + #endregion + + #region IEditableCollection members + /// <summary> + /// Remove all items and reset size of internal array container. + /// </summary> + [Tested] + public virtual void Clear() + { + updatecheck(); + array = new T[8]; + size = 0; + } + + + /// <summary> + /// Create an array containing (copies) of the items of this collection in enumeration order. + /// </summary> + /// <returns>The new array</returns> + [Tested] + public override T[] ToArray() + { + T[] res = new T[size]; + + Array.Copy(array, offset, res, 0, size); + return res; + } + + + /// <summary> + /// Perform an internal consistency (invariant) test on the array base. + /// </summary> + /// <returns>True if test succeeds.</returns> + [Tested] + public virtual bool Check() + { + bool retval = true; + + if (size > array.Length) + { + Console.WriteLine("Bad size ({0}) > array.Length ({1})", size, array.Length); + return false; + } + + for (int i = 0; i < size; i++) + { + if ((object)(array[i]) == null) + { + Console.WriteLine("Bad element: null at index {0}", i); + return false; + } + } + + return retval; + } + + #endregion + + #region IDirectedCollection<T> Members + + /// <summary> + /// Create a directed collection with the same contents as this one, but + /// opposite enumeration sequence. + /// </summary> + /// <returns>The mirrored collection.</returns> + [Tested] + public override IDirectedCollectionValue<T> Backwards() { return this[0, size].Backwards(); } + + #endregion + + /// <summary> + /// Choose some item of this collection. The result is the last item in the internal array, + /// making it efficient to remove. + /// </summary> + /// <exception cref="NoSuchItemException">if collection is empty.</exception> + /// <returns></returns> + public override T Choose() { if (size > 0) return array[size - 1]; throw new NoSuchItemException(); } + + #region IEnumerable<T> Members + /// <summary> + /// Create an enumerator for this array based collection. + /// </summary> + /// <returns>The enumerator</returns> + [Tested] + public override SCG.IEnumerator<T> GetEnumerator() + { + int thestamp = stamp, theend = size + offset, thestart = offset; + + for (int i = thestart; i < theend; i++) + { + modifycheck(thestamp); + yield return array[i]; + } + } + #endregion + + #region Range nested class + /// <summary> + /// A helper class for defining results of interval queries on array based collections. + /// </summary> + protected class Range : DirectedCollectionValueBase<T>, IDirectedCollectionValue<T> + { + int start, count, delta, stamp; + + ArrayBase<T> thebase; + + + internal Range(ArrayBase<T> thebase, int start, int count, bool forwards) + { + this.thebase = thebase; stamp = thebase.stamp; + delta = forwards ? 1 : -1; + this.start = start + thebase.offset; this.count = count; + } + + /// <summary> + /// + /// </summary> + /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception> + /// <value>True if this collection is empty.</value> + public override bool IsEmpty { get { thebase.modifycheck(stamp); return count == 0; } } + + + /// <summary> + /// + /// </summary> + /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception> + /// <value>The number of items in the range</value> + [Tested] + public override int Count { [Tested]get { thebase.modifycheck(stamp); return count; } } + + /// <summary> + /// The value is symbolic indicating the type of asymptotic complexity + /// in terms of the size of this collection (worst-case or amortized as + /// relevant). + /// </summary> + /// <value>A characterization of the speed of the + /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception> + /// <code>Count</code> property in this collection.</value> + public override Speed CountSpeed { get { thebase.modifycheck(stamp); return Speed.Constant; } } + + /// <summary> + /// Choose some item of this collection. + /// </summary> + /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception> + /// <exception cref="NoSuchItemException">if range is empty.</exception> + /// <returns></returns> + public override T Choose() + { + thebase.modifycheck(stamp); + if (count == 0) + throw new NoSuchItemException(); + return thebase.array[start]; + } + + + /// <summary> + /// Create an enumerator for this range of an array based collection. + /// </summary> + /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception> + /// <returns>The enumerator</returns> + [Tested] + public override SCG.IEnumerator<T> GetEnumerator() + { + for (int i = 0; i < count; i++) + { + thebase.modifycheck(stamp); + yield return thebase.array[start + delta * i]; + } + } + + + /// <summary> + /// Create a araay collection range with the same contents as this one, but + /// opposite enumeration sequence. + /// </summary> + /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception> + /// <returns>The mirrored collection.</returns> + [Tested] + public override IDirectedCollectionValue<T> Backwards() + { + thebase.modifycheck(stamp); + + Range res = (Range)MemberwiseClone(); + + res.delta = -delta; + res.start = start + (count - 1) * delta; + return res; + } + + + IDirectedEnumerable<T> C5.IDirectedEnumerable<T>.Backwards() + { + return Backwards(); + } + + + /// <summary> + /// <code>Forwards</code> if same, else <code>Backwards</code> + /// </summary> + /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception> + /// <value>The enumeration direction relative to the original collection.</value> + [Tested] + public override EnumerationDirection Direction + { + [Tested] + get + { + thebase.modifycheck(stamp); + return delta > 0 ? EnumerationDirection.Forwards : EnumerationDirection.Backwards; + } + } + } + #endregion + } +} diff --git a/mcs/class/Mono.C5/C5/Comparer.cs b/mcs/class/Mono.C5/C5/Comparer.cs index 8b68baebe83..931b1f3699a 100644 --- a/mcs/class/Mono.C5/C5/Comparer.cs +++ b/mcs/class/Mono.C5/C5/Comparer.cs @@ -1,181 +1,181 @@ -/*
- Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-
-using C5;
-using System;
-using System.Reflection;
-using System.Reflection.Emit;
-using System.Diagnostics;
-using SCG = System.Collections.Generic;
-
-namespace C5
-{
- /// <summary>
- /// A default item comparer for an item type that is either generic (IComparable<T>)
- /// or ordinarily (System.IComparable) comparable.
- /// </summary>
- public static class Comparer<T>
- {
- readonly static Type naturalComparerO = typeof(NaturalComparerO<>);
-
- readonly static Type naturalComparer = typeof(NaturalComparer<>);
-
- static SCG.IComparer<T> cachedComparer = null;
-
- /// <summary>
- /// Create a default comparer.
- /// <para>The IComparer[T] object is constructed when this class is initialised, i.e.
- /// its static constructors called. Thus, the property will be the same object
- /// for the duration of an invocation of the runtime, but a value serialized in
- /// another invocation and deserialized here will not be the same object.</para>
- /// </summary>
- /// <exception cref="NotComparableException">If T is not comparable</exception>
- /// <value>The comparer</value>
- [Tested]
- public static SCG.IComparer<T> Default
- {
- get
- {
- if (cachedComparer != null)
- return cachedComparer;
-
- Type t = typeof(T);
-
- if (t.IsValueType)
- {
- if (t.Equals(typeof(char)))
- return cachedComparer = (SCG.IComparer<T>)(new CharComparer());
-
- if (t.Equals(typeof(sbyte)))
- return cachedComparer = (SCG.IComparer<T>)(new SByteComparer());
-
- if (t.Equals(typeof(byte)))
- return cachedComparer = (SCG.IComparer<T>)(new ByteComparer());
-
- if (t.Equals(typeof(short)))
- return cachedComparer = (SCG.IComparer<T>)(new ShortComparer());
-
- if (t.Equals(typeof(ushort)))
- return cachedComparer = (SCG.IComparer<T>)(new UShortComparer());
-
- if (t.Equals(typeof(int)))
- return cachedComparer = (SCG.IComparer<T>)(new IntComparer());
-
- if (t.Equals(typeof(uint)))
- return cachedComparer = (SCG.IComparer<T>)(new UIntComparer());
-
- if (t.Equals(typeof(long)))
- return cachedComparer = (SCG.IComparer<T>)(new LongComparer());
-
- if (t.Equals(typeof(ulong)))
- return cachedComparer = (SCG.IComparer<T>)(new ULongComparer());
-
- if (t.Equals(typeof(float)))
- return cachedComparer = (SCG.IComparer<T>)(new FloatComparer());
-
- if (t.Equals(typeof(double)))
- return cachedComparer = (SCG.IComparer<T>)(new DoubleComparer());
-
- if (t.Equals(typeof(decimal)))
- return cachedComparer = (SCG.IComparer<T>)(new DecimalComparer());
- }
-
- if (typeof(IComparable<T>).IsAssignableFrom(t))
- {
- Type c = naturalComparer.MakeGenericType(new Type[] { t });
-
- return cachedComparer = (SCG.IComparer<T>)(c.GetConstructor(System.Type.EmptyTypes).Invoke(null));
- }
-
- if (t.GetInterface("System.IComparable") != null)
- {
- Type c = naturalComparerO.MakeGenericType(new Type[] { t });
-
- return cachedComparer = (SCG.IComparer<T>)(c.GetConstructor(System.Type.EmptyTypes).Invoke(null));
- }
-
- throw new NotComparableException(String.Format("Cannot make comparer for type {0}", t));
- }
- }
- }
-
- /// <summary>
- /// A natural generic IComparer for an IComparable<T> item type
- /// </summary>
- /// <typeparam name="T"></typeparam>
- [Serializable]
- public class NaturalComparer<T> : SCG.IComparer<T>
- where T : IComparable<T>
- {
- /// <summary>
- /// Compare two items
- /// </summary>
- /// <param name="item1">First item</param>
- /// <param name="item2">Second item</param>
- /// <returns>item1 <=> item2</returns>
- [Tested]
- public int Compare(T item1, T item2) { return item1 != null ? item1.CompareTo(item2) : item2 != null ? -1 : 0; }
- }
-
- /// <summary>
- /// A natural generic IComparer for a System.IComparable item type
- /// </summary>
- /// <typeparam name="T"></typeparam>
- [Serializable]
- public class NaturalComparerO<T> : SCG.IComparer<T> where T : System.IComparable
- {
- /// <summary>
- /// Compare two items
- /// </summary>
- /// <param name="item1">First item</param>
- /// <param name="item2">Second item</param>
- /// <returns>item1 <=> item2</returns>
- [Tested]
- public int Compare(T item1, T item2) { return item1 != null ? item1.CompareTo(item2) : item2 != null ? -1 : 0; }
- }
-
- /// <summary>
- /// A generic comparer for type T based on a Comparison[T] delegate
- /// </summary>
- /// <typeparam name="T"></typeparam>
- [Serializable]
- public class DelegateComparer<T> : SCG.IComparer<T>
- {
- readonly Comparison<T> cmp;
- /// <summary>
- ///
- /// </summary>
- /// <param name="comparison"></param>
- public DelegateComparer(Comparison<T> comparison)
- {
- if (comparison == null)
- throw new NullReferenceException("Comparison cannot be null");
- this.cmp = comparison;
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="item1">First item</param>
- /// <param name="item2">Second item</param>
- /// <returns>item1 <=> item2</returns>
- public int Compare(T item1, T item2) { return cmp(item1, item2); }
- }
+/* + Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +using C5; +using System; +using System.Reflection; +using System.Reflection.Emit; +using System.Diagnostics; +using SCG = System.Collections.Generic; + +namespace C5 +{ + /// <summary> + /// A default item comparer for an item type that is either generic (IComparable<T>) + /// or ordinarily (System.IComparable) comparable. + /// </summary> + public static class Comparer<T> + { + readonly static Type naturalComparerO = typeof(NaturalComparerO<>); + + readonly static Type naturalComparer = typeof(NaturalComparer<>); + + static SCG.IComparer<T> cachedComparer = null; + + /// <summary> + /// Create a default comparer. + /// <para>The IComparer[T] object is constructed when this class is initialised, i.e. + /// its static constructors called. Thus, the property will be the same object + /// for the duration of an invocation of the runtime, but a value serialized in + /// another invocation and deserialized here will not be the same object.</para> + /// </summary> + /// <exception cref="NotComparableException">If T is not comparable</exception> + /// <value>The comparer</value> + [Tested] + public static SCG.IComparer<T> Default + { + get + { + if (cachedComparer != null) + return cachedComparer; + + Type t = typeof(T); + + if (t.IsValueType) + { + if (t.Equals(typeof(char))) + return cachedComparer = (SCG.IComparer<T>)(new CharComparer()); + + if (t.Equals(typeof(sbyte))) + return cachedComparer = (SCG.IComparer<T>)(new SByteComparer()); + + if (t.Equals(typeof(byte))) + return cachedComparer = (SCG.IComparer<T>)(new ByteComparer()); + + if (t.Equals(typeof(short))) + return cachedComparer = (SCG.IComparer<T>)(new ShortComparer()); + + if (t.Equals(typeof(ushort))) + return cachedComparer = (SCG.IComparer<T>)(new UShortComparer()); + + if (t.Equals(typeof(int))) + return cachedComparer = (SCG.IComparer<T>)(new IntComparer()); + + if (t.Equals(typeof(uint))) + return cachedComparer = (SCG.IComparer<T>)(new UIntComparer()); + + if (t.Equals(typeof(long))) + return cachedComparer = (SCG.IComparer<T>)(new LongComparer()); + + if (t.Equals(typeof(ulong))) + return cachedComparer = (SCG.IComparer<T>)(new ULongComparer()); + + if (t.Equals(typeof(float))) + return cachedComparer = (SCG.IComparer<T>)(new FloatComparer()); + + if (t.Equals(typeof(double))) + return cachedComparer = (SCG.IComparer<T>)(new DoubleComparer()); + + if (t.Equals(typeof(decimal))) + return cachedComparer = (SCG.IComparer<T>)(new DecimalComparer()); + } + + if (typeof(IComparable<T>).IsAssignableFrom(t)) + { + Type c = naturalComparer.MakeGenericType(new Type[] { t }); + + return cachedComparer = (SCG.IComparer<T>)(c.GetConstructor(System.Type.EmptyTypes).Invoke(null)); + } + + if (t.GetInterface("System.IComparable") != null) + { + Type c = naturalComparerO.MakeGenericType(new Type[] { t }); + + return cachedComparer = (SCG.IComparer<T>)(c.GetConstructor(System.Type.EmptyTypes).Invoke(null)); + } + + throw new NotComparableException(String.Format("Cannot make comparer for type {0}", t)); + } + } + } + + /// <summary> + /// A natural generic IComparer for an IComparable<T> item type + /// </summary> + /// <typeparam name="T"></typeparam> + [Serializable] + public class NaturalComparer<T> : SCG.IComparer<T> + where T : IComparable<T> + { + /// <summary> + /// Compare two items + /// </summary> + /// <param name="item1">First item</param> + /// <param name="item2">Second item</param> + /// <returns>item1 <=> item2</returns> + [Tested] + public int Compare(T item1, T item2) { return item1 != null ? item1.CompareTo(item2) : item2 != null ? -1 : 0; } + } + + /// <summary> + /// A natural generic IComparer for a System.IComparable item type + /// </summary> + /// <typeparam name="T"></typeparam> + [Serializable] + public class NaturalComparerO<T> : SCG.IComparer<T> where T : System.IComparable + { + /// <summary> + /// Compare two items + /// </summary> + /// <param name="item1">First item</param> + /// <param name="item2">Second item</param> + /// <returns>item1 <=> item2</returns> + [Tested] + public int Compare(T item1, T item2) { return item1 != null ? item1.CompareTo(item2) : item2 != null ? -1 : 0; } + } + + /// <summary> + /// A generic comparer for type T based on a Comparison[T] delegate + /// </summary> + /// <typeparam name="T"></typeparam> + [Serializable] + public class DelegateComparer<T> : SCG.IComparer<T> + { + readonly Comparison<T> cmp; + /// <summary> + /// + /// </summary> + /// <param name="comparison"></param> + public DelegateComparer(Comparison<T> comparison) + { + if (comparison == null) + throw new NullReferenceException("Comparison cannot be null"); + this.cmp = comparison; + } + /// <summary> + /// + /// </summary> + /// <param name="item1">First item</param> + /// <param name="item2">Second item</param> + /// <returns>item1 <=> item2</returns> + public int Compare(T item1, T item2) { return cmp(item1, item2); } + } }
\ No newline at end of file diff --git a/mcs/class/Mono.C5/C5/Delegates.cs b/mcs/class/Mono.C5/C5/Delegates.cs index 36792d8555b..0329bfb9dcc 100644 --- a/mcs/class/Mono.C5/C5/Delegates.cs +++ b/mcs/class/Mono.C5/C5/Delegates.cs @@ -1,131 +1,131 @@ -/*
- Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-
-using C5;
-using System;
-using System.Reflection;
-using System.Reflection.Emit;
-using System.Diagnostics;
-
-#pragma warning disable 1711
-namespace C5
-{
- /// <summary>
- ///
- /// </summary>
- public delegate void Act();
- /// <summary>
- /// <para>
- /// The type Act[T] corresponds to System.Action[T] in the .Net
- /// Framework class library.
- ///</para>
- /// </summary>
- /// <typeparam name="A1"></typeparam>
- /// <param name="x1"></param>
- public delegate void Act<A1>(A1 x1);
- /// <summary>
- ///
- /// </summary>
- /// <typeparam name="A1"></typeparam>
- /// <typeparam name="A2"></typeparam>
- /// <param name="x1"></param>
- /// <param name="x2"></param>
- public delegate void Act<A1, A2>(A1 x1, A2 x2);
- /// <summary>
- ///
- /// </summary>
- /// <typeparam name="A1"></typeparam>
- /// <typeparam name="A2"></typeparam>
- /// <typeparam name="A3"></typeparam>
- /// <param name="x1"></param>
- /// <param name="x2"></param>
- /// <param name="x3"></param>
- public delegate void Act<A1, A2, A3>(A1 x1, A2 x2, A3 x3);
- /// <summary>
- ///
- /// </summary>
- /// <typeparam name="A1"></typeparam>
- /// <typeparam name="A2"></typeparam>
- /// <typeparam name="A3"></typeparam>
- /// <typeparam name="A4"></typeparam>
- /// <param name="x1"></param>
- /// <param name="x2"></param>
- /// <param name="x3"></param>
- /// <param name="x4"></param>
- public delegate void Act<A1, A2, A3, A4>(A1 x1, A2 x2, A3 x3, A4 x4);
-
- /// <summary>
- ///
- /// </summary>
- /// <typeparam name="R"></typeparam>
- /// <returns></returns>
- public delegate R Fun<R>();
- /// <summary>
- /// Delegate type Fun[A1,R] is the type of functions (methods) from A1
- /// to R, used to compute some transformation for a given collection
- /// item.
- /// <para>
- /// The type Fun[T,U] corresponds to System.Converter[T,U] in the .Net
- /// Framework class library, and the type Fun[T,bool] corresponds
- /// System.Predicate[T] in the .Net Framework class library.</para>
- /// </summary>
- /// <typeparam name="A1"></typeparam>
- /// <typeparam name="R"></typeparam>
- /// <param name="x1"></param>
- /// <returns></returns>
- public delegate R Fun<A1, R>(A1 x1);
- /// <summary>
- ///
- /// </summary>
- /// <typeparam name="A1"></typeparam>
- /// <typeparam name="A2"></typeparam>
- /// <typeparam name="R"></typeparam>
- /// <param name="x1"></param>
- /// <param name="x2"></param>
- /// <returns></returns>
- public delegate R Fun<A1, A2, R>(A1 x1, A2 x2);
- /// <summary>
- ///
- /// </summary>
- /// <typeparam name="A1"></typeparam>
- /// <typeparam name="A2"></typeparam>
- /// <typeparam name="A3"></typeparam>
- /// <typeparam name="R"></typeparam>
- /// <param name="x1"></param>
- /// <param name="x2"></param>
- /// <param name="x3"></param>
- /// <returns></returns>
- public delegate R Fun<A1, A2, A3, R>(A1 x1, A2 x2, A3 x3);
- /// <summary>
- ///
- /// </summary>
- /// <typeparam name="A1"></typeparam>
- /// <typeparam name="A2"></typeparam>
- /// <typeparam name="A3"></typeparam>
- /// <typeparam name="A4"></typeparam>
- /// <typeparam name="R"></typeparam>
- /// <param name="x1"></param>
- /// <param name="x2"></param>
- /// <param name="x3"></param>
- /// <param name="x4"></param>
- /// <returns></returns>
- public delegate R Fun<A1, A2, A3, A4, R>(A1 x1, A2 x2, A3 x3, A4 x4);
-}
+/* + Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +using C5; +using System; +using System.Reflection; +using System.Reflection.Emit; +using System.Diagnostics; + +#pragma warning disable 1711 +namespace C5 +{ + /// <summary> + /// + /// </summary> + public delegate void Act(); + /// <summary> + /// <para> + /// The type Act[T] corresponds to System.Action[T] in the .Net + /// Framework class library. + ///</para> + /// </summary> + /// <typeparam name="A1"></typeparam> + /// <param name="x1"></param> + public delegate void Act<A1>(A1 x1); + /// <summary> + /// + /// </summary> + /// <typeparam name="A1"></typeparam> + /// <typeparam name="A2"></typeparam> + /// <param name="x1"></param> + /// <param name="x2"></param> + public delegate void Act<A1, A2>(A1 x1, A2 x2); + /// <summary> + /// + /// </summary> + /// <typeparam name="A1"></typeparam> + /// <typeparam name="A2"></typeparam> + /// <typeparam name="A3"></typeparam> + /// <param name="x1"></param> + /// <param name="x2"></param> + /// <param name="x3"></param> + public delegate void Act<A1, A2, A3>(A1 x1, A2 x2, A3 x3); + /// <summary> + /// + /// </summary> + /// <typeparam name="A1"></typeparam> + /// <typeparam name="A2"></typeparam> + /// <typeparam name="A3"></typeparam> + /// <typeparam name="A4"></typeparam> + /// <param name="x1"></param> + /// <param name="x2"></param> + /// <param name="x3"></param> + /// <param name="x4"></param> + public delegate void Act<A1, A2, A3, A4>(A1 x1, A2 x2, A3 x3, A4 x4); + + /// <summary> + /// + /// </summary> + /// <typeparam name="R"></typeparam> + /// <returns></returns> + public delegate R Fun<R>(); + /// <summary> + /// Delegate type Fun[A1,R] is the type of functions (methods) from A1 + /// to R, used to compute some transformation for a given collection + /// item. + /// <para> + /// The type Fun[T,U] corresponds to System.Converter[T,U] in the .Net + /// Framework class library, and the type Fun[T,bool] corresponds + /// System.Predicate[T] in the .Net Framework class library.</para> + /// </summary> + /// <typeparam name="A1"></typeparam> + /// <typeparam name="R"></typeparam> + /// <param name="x1"></param> + /// <returns></returns> + public delegate R Fun<A1, R>(A1 x1); + /// <summary> + /// + /// </summary> + /// <typeparam name="A1"></typeparam> + /// <typeparam name="A2"></typeparam> + /// <typeparam name="R"></typeparam> + /// <param name="x1"></param> + /// <param name="x2"></param> + /// <returns></returns> + public delegate R Fun<A1, A2, R>(A1 x1, A2 x2); + /// <summary> + /// + /// </summary> + /// <typeparam name="A1"></typeparam> + /// <typeparam name="A2"></typeparam> + /// <typeparam name="A3"></typeparam> + /// <typeparam name="R"></typeparam> + /// <param name="x1"></param> + /// <param name="x2"></param> + /// <param name="x3"></param> + /// <returns></returns> + public delegate R Fun<A1, A2, A3, R>(A1 x1, A2 x2, A3 x3); + /// <summary> + /// + /// </summary> + /// <typeparam name="A1"></typeparam> + /// <typeparam name="A2"></typeparam> + /// <typeparam name="A3"></typeparam> + /// <typeparam name="A4"></typeparam> + /// <typeparam name="R"></typeparam> + /// <param name="x1"></param> + /// <param name="x2"></param> + /// <param name="x3"></param> + /// <param name="x4"></param> + /// <returns></returns> + public delegate R Fun<A1, A2, A3, A4, R>(A1 x1, A2 x2, A3 x3, A4 x4); +} diff --git a/mcs/class/Mono.C5/C5/Dictionaries.cs b/mcs/class/Mono.C5/C5/Dictionaries.cs index 58935240c7d..54899e2c9c8 100644 --- a/mcs/class/Mono.C5/C5/Dictionaries.cs +++ b/mcs/class/Mono.C5/C5/Dictionaries.cs @@ -1,1383 +1,1383 @@ -/*
- Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-
-using System;
-using System.Diagnostics;
-using SCG = System.Collections.Generic;
-namespace C5
-{
- /// <summary>
- /// An entry in a dictionary from K to V.
- /// </summary>
- [Serializable]
- public struct KeyValuePair<K, V> : IEquatable<KeyValuePair<K, V>>, IShowable
- {
- /// <summary>
- /// The key field of the entry
- /// </summary>
- public K Key;
-
- /// <summary>
- /// The value field of the entry
- /// </summary>
- public V Value;
-
- /// <summary>
- /// Create an entry with specified key and value
- /// </summary>
- /// <param name="key">The key</param>
- /// <param name="value">The value</param>
- public KeyValuePair(K key, V value) { Key = key; Value = value; }
-
-
- /// <summary>
- /// Create an entry with a specified key. The value will be the default value of type <code>V</code>.
- /// </summary>
- /// <param name="key">The key</param>
- public KeyValuePair(K key) { Key = key; Value = default(V); }
-
-
- /// <summary>
- /// Pretty print an entry
- /// </summary>
- /// <returns>(key, value)</returns>
- [Tested]
- public override string ToString() { return "(" + Key + ", " + Value + ")"; }
-
-
- /// <summary>
- /// Check equality of entries.
- /// </summary>
- /// <param name="obj">The other object</param>
- /// <returns>True if obj is an entry of the same type and has the same key and value</returns>
- [Tested]
- public override bool Equals(object obj)
- {
- if (!(obj is KeyValuePair<K, V>))
- return false;
- KeyValuePair<K, V> other = (KeyValuePair<K, V>)obj;
- return Equals(other);
- }
-
-
- /// <summary>
- /// Get the hash code of the pair.
- /// </summary>
- /// <returns>The hash code</returns>
- [Tested]
- public override int GetHashCode() { return EqualityComparer<K>.Default.GetHashCode(Key) + 13984681 * EqualityComparer<V>.Default.GetHashCode(Value); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="other"></param>
- /// <returns></returns>
- public bool Equals(KeyValuePair<K, V> other)
- {
- return EqualityComparer<K>.Default.Equals(Key, other.Key) && EqualityComparer<V>.Default.Equals(Value, other.Value);
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="pair1"></param>
- /// <param name="pair2"></param>
- /// <returns></returns>
- public static bool operator ==(KeyValuePair<K, V> pair1, KeyValuePair<K, V> pair2) { return pair1.Equals(pair2); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="pair1"></param>
- /// <param name="pair2"></param>
- /// <returns></returns>
- public static bool operator !=(KeyValuePair<K, V> pair1, KeyValuePair<K, V> pair2) { return !pair1.Equals(pair2); }
-
- #region IShowable Members
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="stringbuilder"></param>
- /// <param name="formatProvider"></param>
- /// <param name="rest"></param>
- /// <returns></returns>
- public bool Show(System.Text.StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider)
- {
- if (rest < 0)
- return false;
- if (!Showing.Show(Key, stringbuilder, ref rest, formatProvider))
- return false;
- stringbuilder.Append(" => ");
- rest -= 4;
- if (!Showing.Show(Value, stringbuilder, ref rest, formatProvider))
- return false;
- return rest >= 0;
- }
- #endregion
-
- #region IFormattable Members
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="format"></param>
- /// <param name="formatProvider"></param>
- /// <returns></returns>
- public string ToString(string format, IFormatProvider formatProvider)
- {
- return Showing.ShowString(this, format, formatProvider);
- }
-
- #endregion
- }
-
-
-
- /// <summary>
- /// Default comparer for dictionary entries in a sorted dictionary.
- /// Entry comparisons only look at keys and uses an externally defined comparer for that.
- /// </summary>
- [Serializable]
- public class KeyValuePairComparer<K, V> : SCG.IComparer<KeyValuePair<K, V>>
- {
- SCG.IComparer<K> comparer;
-
-
- /// <summary>
- /// Create an entry comparer for a item comparer of the keys
- /// </summary>
- /// <param name="comparer">Comparer of keys</param>
- public KeyValuePairComparer(SCG.IComparer<K> comparer)
- {
- if (comparer == null)
- throw new NullReferenceException();
- this.comparer = comparer;
- }
-
-
- /// <summary>
- /// Compare two entries
- /// </summary>
- /// <param name="entry1">First entry</param>
- /// <param name="entry2">Second entry</param>
- /// <returns>The result of comparing the keys</returns>
- [Tested]
- public int Compare(KeyValuePair<K, V> entry1, KeyValuePair<K, V> entry2)
- { return comparer.Compare(entry1.Key, entry2.Key); }
- }
-
-
-
- /// <summary>
- /// Default equalityComparer for dictionary entries.
- /// Operations only look at keys and uses an externaly defined equalityComparer for that.
- /// </summary>
- [Serializable]
- public sealed class KeyValuePairEqualityComparer<K, V> : SCG.IEqualityComparer<KeyValuePair<K, V>>
- {
- SCG.IEqualityComparer<K> keyequalityComparer;
-
-
- /// <summary>
- /// Create an entry equalityComparer using the default equalityComparer for keys
- /// </summary>
- public KeyValuePairEqualityComparer() { keyequalityComparer = EqualityComparer<K>.Default; }
-
-
- /// <summary>
- /// Create an entry equalityComparer from a specified item equalityComparer for the keys
- /// </summary>
- /// <param name="keyequalityComparer">The key equalityComparer</param>
- public KeyValuePairEqualityComparer(SCG.IEqualityComparer<K> keyequalityComparer)
- {
- if (keyequalityComparer == null)
- throw new NullReferenceException("Key equality comparer cannot be null");
- this.keyequalityComparer = keyequalityComparer;
- }
-
-
- /// <summary>
- /// Get the hash code of the entry
- /// </summary>
- /// <param name="entry">The entry</param>
- /// <returns>The hash code of the key</returns>
- [Tested]
- public int GetHashCode(KeyValuePair<K, V> entry) { return keyequalityComparer.GetHashCode(entry.Key); }
-
-
- /// <summary>
- /// Test two entries for equality
- /// </summary>
- /// <param name="entry1">First entry</param>
- /// <param name="entry2">Second entry</param>
- /// <returns>True if keys are equal</returns>
- [Tested]
- public bool Equals(KeyValuePair<K, V> entry1, KeyValuePair<K, V> entry2)
- { return keyequalityComparer.Equals(entry1.Key, entry2.Key); }
- }
-
-
-
- /// <summary>
- /// A base class for implementing a dictionary based on a set collection implementation.
- /// <i>See the source code for <see cref="T:C5.HashDictionary`2"/> for an example</i>
- ///
- /// </summary>
- [Serializable]
- public abstract class DictionaryBase<K, V> : CollectionValueBase<KeyValuePair<K, V>>, IDictionary<K, V>
- {
- /// <summary>
- /// The set collection of entries underlying this dictionary implementation
- /// </summary>
- protected ICollection<KeyValuePair<K, V>> pairs;
-
- SCG.IEqualityComparer<K> keyequalityComparer;
-
- #region Events
- ProxyEventBlock<KeyValuePair<K, V>> eventBlock;
-
- /// <summary>
- /// The change event. Will be raised for every change operation on the collection.
- /// </summary>
- public override event CollectionChangedHandler<KeyValuePair<K, V>> CollectionChanged
- {
- add { (eventBlock ?? (eventBlock = new ProxyEventBlock<KeyValuePair<K, V>>(this, pairs))).CollectionChanged += value; }
- remove { if (eventBlock != null) eventBlock.CollectionChanged -= value; }
- }
-
- /// <summary>
- /// The change event. Will be raised for every change operation on the collection.
- /// </summary>
- public override event CollectionClearedHandler<KeyValuePair<K, V>> CollectionCleared
- {
- add { (eventBlock ?? (eventBlock = new ProxyEventBlock<KeyValuePair<K, V>>(this, pairs))).CollectionCleared += value; }
- remove { if (eventBlock != null) eventBlock.CollectionCleared -= value; }
- }
-
- /// <summary>
- /// The item added event. Will be raised for every individual addition to the collection.
- /// </summary>
- public override event ItemsAddedHandler<KeyValuePair<K, V>> ItemsAdded
- {
- add { (eventBlock ?? (eventBlock = new ProxyEventBlock<KeyValuePair<K, V>>(this, pairs))).ItemsAdded += value; }
- remove { if (eventBlock != null) eventBlock.ItemsAdded -= value; }
- }
-
- /// <summary>
- /// The item added event. Will be raised for every individual removal from the collection.
- /// </summary>
- public override event ItemsRemovedHandler<KeyValuePair<K, V>> ItemsRemoved
- {
- add { (eventBlock ?? (eventBlock = new ProxyEventBlock<KeyValuePair<K, V>>(this, pairs))).ItemsRemoved += value; }
- remove { if (eventBlock != null) eventBlock.ItemsRemoved -= value; }
- }
-
- /// <summary>
- ///
- /// </summary>
- public override EventTypeEnum ListenableEvents
- {
- get
- {
- return EventTypeEnum.Basic;
- }
- }
-
- /// <summary>
- ///
- /// </summary>
- public override EventTypeEnum ActiveEvents
- {
- get
- {
- return pairs.ActiveEvents;
- }
- }
-
- #endregion
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="keyequalityComparer"></param>
- protected DictionaryBase(SCG.IEqualityComparer<K> keyequalityComparer)
- {
- if (keyequalityComparer == null)
- throw new NullReferenceException("Key equality comparer cannot be null");
- this.keyequalityComparer = keyequalityComparer;
- }
-
- #region IDictionary<K,V> Members
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public virtual SCG.IEqualityComparer<K> EqualityComparer { get { return keyequalityComparer; } }
-
-
- /// <summary>
- /// Add a new (key, value) pair (a mapping) to the dictionary.
- /// </summary>
- /// <exception cref="DuplicateNotAllowedException"> if there already is an entry with the same key. </exception>
- /// <param name="key">Key to add</param>
- /// <param name="value">Value to add</param>
- [Tested]
- public virtual void Add(K key, V value)
- {
- KeyValuePair<K, V> p = new KeyValuePair<K, V>(key, value);
-
- if (!pairs.Add(p))
- throw new DuplicateNotAllowedException("Key being added: '" + key + "'");
- }
-
- /// <summary>
- /// Add the entries from a collection of <see cref="T:C5.KeyValuePair`2"/> pairs to this dictionary.
- /// <para><b>TODO: add restrictions L:K and W:V when the .Net SDK allows it </b></para>
- /// </summary>
- /// <exception cref="DuplicateNotAllowedException">
- /// If the input contains duplicate keys or a key already present in this dictionary.</exception>
- /// <param name="entries"></param>
- public virtual void AddAll<L, W>(SCG.IEnumerable<KeyValuePair<L, W>> entries)
- where L : K
- where W : V
- {
- foreach (KeyValuePair<L, W> pair in entries)
- {
- KeyValuePair<K, V> p = new KeyValuePair<K, V>(pair.Key, pair.Value);
- if (!pairs.Add(p))
- throw new DuplicateNotAllowedException("Key being added: '" + pair.Key + "'");
- }
- }
-
- /// <summary>
- /// Remove an entry with a given key from the dictionary
- /// </summary>
- /// <param name="key">The key of the entry to remove</param>
- /// <returns>True if an entry was found (and removed)</returns>
- [Tested]
- public virtual bool Remove(K key)
- {
- KeyValuePair<K, V> p = new KeyValuePair<K, V>(key);
-
- return pairs.Remove(p);
- }
-
-
- /// <summary>
- /// Remove an entry with a given key from the dictionary and report its value.
- /// </summary>
- /// <param name="key">The key of the entry to remove</param>
- /// <param name="value">On exit, the value of the removed entry</param>
- /// <returns>True if an entry was found (and removed)</returns>
- [Tested]
- public virtual bool Remove(K key, out V value)
- {
- KeyValuePair<K, V> p = new KeyValuePair<K, V>(key);
-
- if (pairs.Remove(p, out p))
- {
- value = p.Value;
- return true;
- }
- else
- {
- value = default(V);
- return false;
- }
- }
-
-
- /// <summary>
- /// Remove all entries from the dictionary
- /// </summary>
- [Tested]
- public virtual void Clear() { pairs.Clear(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public virtual Speed ContainsSpeed { get { return pairs.ContainsSpeed; } }
-
- /// <summary>
- /// Check if there is an entry with a specified key
- /// </summary>
- /// <param name="key">The key to look for</param>
- /// <returns>True if key was found</returns>
- [Tested]
- public virtual bool Contains(K key)
- {
- KeyValuePair<K, V> p = new KeyValuePair<K, V>(key);
-
- return pairs.Contains(p);
- }
-
- [Serializable]
- class LiftedEnumerable<H> : SCG.IEnumerable<KeyValuePair<K, V>> where H : K
- {
- SCG.IEnumerable<H> keys;
- public LiftedEnumerable(SCG.IEnumerable<H> keys) { this.keys = keys; }
- public SCG.IEnumerator<KeyValuePair<K, V>> GetEnumerator() { foreach (H key in keys) yield return new KeyValuePair<K, V>(key); }
-
- #region IEnumerable Members
-
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
- {
- throw new Exception("The method or operation is not implemented.");
- }
-
- #endregion
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="keys"></param>
- /// <returns></returns>
- public virtual bool ContainsAll<H>(SCG.IEnumerable<H> keys) where H : K
- {
- return pairs.ContainsAll(new LiftedEnumerable<H>(keys));
- }
-
- /// <summary>
- /// Check if there is an entry with a specified key and report the corresponding
- /// value if found. This can be seen as a safe form of "val = this[key]".
- /// </summary>
- /// <param name="key">The key to look for</param>
- /// <param name="value">On exit, the value of the entry</param>
- /// <returns>True if key was found</returns>
- [Tested]
- public virtual bool Find(K key, out V value)
- {
- return Find(ref key, out value);
- }
- /// <summary>
- /// Check if there is an entry with a specified key and report the corresponding
- /// value if found. This can be seen as a safe form of "val = this[key]".
- /// </summary>
- /// <param name="key">The key to look for</param>
- /// <param name="value">On exit, the value of the entry</param>
- /// <returns>True if key was found</returns>
- public virtual bool Find(ref K key, out V value)
- {
- KeyValuePair<K, V> p = new KeyValuePair<K, V>(key);
-
- if (pairs.Find(ref p))
- {
- key = p.Key;
- value = p.Value;
- return true;
- }
- else
- {
- value = default(V);
- return false;
- }
- }
-
-
- /// <summary>
- /// Look for a specific key in the dictionary and if found replace the value with a new one.
- /// This can be seen as a non-adding version of "this[key] = val".
- /// </summary>
- /// <param name="key">The key to look for</param>
- /// <param name="value">The new value</param>
- /// <returns>True if key was found</returns>
- [Tested]
- public virtual bool Update(K key, V value)
- {
- KeyValuePair<K, V> p = new KeyValuePair<K, V>(key, value);
-
- return pairs.Update(p);
- }
-
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="key"></param>
- /// <param name="value"></param>
- /// <param name="oldvalue"></param>
- /// <returns></returns>
- public virtual bool Update(K key, V value, out V oldvalue)
- {
- KeyValuePair<K, V> p = new KeyValuePair<K, V>(key, value);
-
- bool retval = pairs.Update(p, out p);
- oldvalue = p.Value;
- return retval;
- }
-
-
- /// <summary>
- /// Look for a specific key in the dictionary. If found, report the corresponding value,
- /// else add an entry with the key and the supplied value.
- /// </summary>
- /// <param name="key">On entry the key to look for</param>
- /// <param name="value">On entry the value to add if the key is not found.
- /// On exit the value found if any.</param>
- /// <returns>True if key was found</returns>
- [Tested]
- public virtual bool FindOrAdd(K key, ref V value)
- {
- KeyValuePair<K, V> p = new KeyValuePair<K, V>(key, value);
-
- if (!pairs.FindOrAdd(ref p))
- return false;
- else
- {
- value = p.Value;
- //key = p.key;
- return true;
- }
- }
-
-
- /// <summary>
- /// Update value in dictionary corresponding to key if found, else add new entry.
- /// More general than "this[key] = val;" by reporting if key was found.
- /// </summary>
- /// <param name="key">The key to look for</param>
- /// <param name="value">The value to add or replace with.</param>
- /// <returns>True if entry was updated.</returns>
- [Tested]
- public virtual bool UpdateOrAdd(K key, V value)
- {
- return pairs.UpdateOrAdd(new KeyValuePair<K, V>(key, value));
- }
-
-
- /// <summary>
- /// Update value in dictionary corresponding to key if found, else add new entry.
- /// More general than "this[key] = val;" by reporting if key was found and the old value if any.
- /// </summary>
- /// <param name="key"></param>
- /// <param name="value"></param>
- /// <param name="oldvalue"></param>
- /// <returns></returns>
- public virtual bool UpdateOrAdd(K key, V value, out V oldvalue)
- {
- KeyValuePair<K, V> p = new KeyValuePair<K, V>(key, value);
- bool retval = pairs.UpdateOrAdd(p, out p);
- oldvalue = p.Value;
- return retval;
- }
-
-
-
- #region Keys,Values support classes
- [Serializable]
- internal class ValuesCollection : CollectionValueBase<V>, ICollectionValue<V>
- {
- ICollection<KeyValuePair<K, V>> pairs;
-
-
- internal ValuesCollection(ICollection<KeyValuePair<K, V>> pairs)
- { this.pairs = pairs; }
-
-
- public override V Choose() { return pairs.Choose().Value; }
-
- [Tested]
- public override SCG.IEnumerator<V> GetEnumerator()
- {
- //Updatecheck is performed by the pairs enumerator
- foreach (KeyValuePair<K, V> p in pairs)
- yield return p.Value;
- }
-
- public override bool IsEmpty { get { return pairs.IsEmpty; } }
-
- [Tested]
- public override int Count { [Tested]get { return pairs.Count; } }
-
- public override Speed CountSpeed { get { return Speed.Constant; } }
- }
-
-
-
- [Serializable]
- internal class KeysCollection : CollectionValueBase<K>, ICollectionValue<K>
- {
- ICollection<KeyValuePair<K, V>> pairs;
-
-
- internal KeysCollection(ICollection<KeyValuePair<K, V>> pairs)
- { this.pairs = pairs; }
-
- public override K Choose() { return pairs.Choose().Key; }
-
- [Tested]
- public override SCG.IEnumerator<K> GetEnumerator()
- {
- foreach (KeyValuePair<K, V> p in pairs)
- yield return p.Key;
- }
-
- public override bool IsEmpty { get { return pairs.IsEmpty; } }
-
- [Tested]
- public override int Count { [Tested]get { return pairs.Count; } }
-
- public override Speed CountSpeed { get { return pairs.CountSpeed; } }
- }
- #endregion
-
- /// <summary>
- ///
- /// </summary>
- /// <value>A collection containg the all the keys of the dictionary</value>
- [Tested]
- public virtual ICollectionValue<K> Keys { [Tested]get { return new KeysCollection(pairs); } }
-
-
- /// <summary>
- ///
- /// </summary>
- /// <value>A collection containing all the values of the dictionary</value>
- [Tested]
- public virtual ICollectionValue<V> Values { [Tested]get { return new ValuesCollection(pairs); } }
-
- /// <summary>
- ///
- /// </summary>
- public virtual Fun<K, V> Fun { get { return delegate(K k) { return this[k]; }; } }
-
- /// <summary>
- /// Indexer by key for dictionary.
- /// <para>The get method will throw an exception if no entry is found. </para>
- /// <para>The set method behaves like <see cref="M:C5.DictionaryBase`2.UpdateOrAdd(`0,`1)"/>.</para>
- /// </summary>
- /// <exception cref="NoSuchItemException"> On get if no entry is found. </exception>
- /// <value>The value corresponding to the key</value>
- [Tested]
- public virtual V this[K key]
- {
- [Tested]
- get
- {
- KeyValuePair<K, V> p = new KeyValuePair<K, V>(key);
-
- if (pairs.Find(ref p))
- return p.Value;
- else
- throw new NoSuchItemException("Key '" + key.ToString() + "' not present in Dictionary");
- }
- [Tested]
- set
- { pairs.UpdateOrAdd(new KeyValuePair<K, V>(key, value)); }
- }
-
-
- /// <summary>
- ///
- /// </summary>
- /// <value>True if dictionary is read only</value>
- [Tested]
- public virtual bool IsReadOnly { [Tested]get { return pairs.IsReadOnly; } }
-
-
- /// <summary>
- /// Check the integrity of the internal data structures of this dictionary.
- /// </summary>
- /// <returns>True if check does not fail.</returns>
- [Tested]
- public virtual bool Check() { return pairs.Check(); }
-
- #endregion
-
- #region ICollectionValue<KeyValuePair<K,V>> Members
-
- /// <summary>
- ///
- /// </summary>
- /// <value>True if this collection is empty.</value>
- public override bool IsEmpty { get { return pairs.IsEmpty; } }
-
-
- /// <summary>
- ///
- /// </summary>
- /// <value>The number of entrues in the dictionary</value>
- [Tested]
- public override int Count { [Tested]get { return pairs.Count; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <value>The number of entrues in the dictionary</value>
- [Tested]
- public override Speed CountSpeed { [Tested]get { return pairs.CountSpeed; } }
-
- /// <summary>
- /// Choose some entry in this Dictionary.
- /// </summary>
- /// <exception cref="NoSuchItemException">if collection is empty.</exception>
- /// <returns></returns>
- public override KeyValuePair<K, V> Choose() { return pairs.Choose(); }
-
- /// <summary>
- /// Create an enumerator for the collection of entries of the dictionary
- /// </summary>
- /// <returns>The enumerator</returns>
- [Tested]
- public override SCG.IEnumerator<KeyValuePair<K, V>> GetEnumerator()
- {
- return pairs.GetEnumerator(); ;
- }
-
- #endregion
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="stringbuilder"></param>
- /// <param name="rest"></param>
- /// <param name="formatProvider"></param>
- /// <returns></returns>
- public override bool Show(System.Text.StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider)
- {
- return Showing.ShowDictionary<K, V>(this, stringbuilder, ref rest, formatProvider);
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public abstract object Clone();
-
- }
-
- /// <summary>
- /// A base class for implementing a sorted dictionary based on a sorted set collection implementation.
- /// <i>See the source code for <see cref="T:C5.TreeDictionary`2"/> for an example</i>
- ///
- /// </summary>
- [Serializable]
- public abstract class SortedDictionaryBase<K, V> : DictionaryBase<K, V>, ISortedDictionary<K, V>
- {
- #region Fields
-
- /// <summary>
- ///
- /// </summary>
- protected ISorted<KeyValuePair<K, V>> sortedpairs;
- SCG.IComparer<K> keycomparer;
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="keycomparer"></param>
- /// <param name="keyequalityComparer"></param>
- protected SortedDictionaryBase(SCG.IComparer<K> keycomparer, SCG.IEqualityComparer<K> keyequalityComparer) : base(keyequalityComparer) { this.keycomparer = keycomparer; }
-
- #endregion
-
- #region ISortedDictionary<K,V> Members
-
- /// <summary>
- /// The key comparer used by this dictionary.
- /// </summary>
- /// <value></value>
- public SCG.IComparer<K> Comparer { get { return keycomparer; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public new ISorted<K> Keys { get { return new SortedKeysCollection(this, sortedpairs, keycomparer, EqualityComparer); } }
-
- /// <summary>
- /// Find the entry in the dictionary whose key is the
- /// predecessor of the specified key.
- /// </summary>
- /// <param name="key">The key</param>
- /// <param name="res">The predecessor, if any</param>
- /// <returns>True if key has a predecessor</returns>
- [Tested]
- public bool TryPredecessor(K key, out KeyValuePair<K, V> res)
- {
- return sortedpairs.TryPredecessor(new KeyValuePair<K, V>(key), out res);
- }
-
- /// <summary>
- /// Find the entry in the dictionary whose key is the
- /// successor of the specified key.
- /// </summary>
- /// <param name="key">The key</param>
- /// <param name="res">The successor, if any</param>
- /// <returns>True if the key has a successor</returns>
- [Tested]
- public bool TrySuccessor(K key, out KeyValuePair<K, V> res)
- {
- return sortedpairs.TrySuccessor(new KeyValuePair<K, V>(key), out res);
- }
-
- /// <summary>
- /// Find the entry in the dictionary whose key is the
- /// weak predecessor of the specified key.
- /// </summary>
- /// <param name="key">The key</param>
- /// <param name="res">The predecessor, if any</param>
- /// <returns>True if key has a weak predecessor</returns>
- [Tested]
- public bool TryWeakPredecessor(K key, out KeyValuePair<K, V> res)
- {
- return sortedpairs.TryWeakPredecessor(new KeyValuePair<K, V>(key), out res);
- }
-
- /// <summary>
- /// Find the entry in the dictionary whose key is the
- /// weak successor of the specified key.
- /// </summary>
- /// <param name="key">The key</param>
- /// <param name="res">The weak successor, if any</param>
- /// <returns>True if the key has a weak successor</returns>
- [Tested]
- public bool TryWeakSuccessor(K key, out KeyValuePair<K, V> res)
- {
- return sortedpairs.TryWeakSuccessor(new KeyValuePair<K, V>(key), out res);
- }
-
- /// <summary>
- /// Get the entry in the dictionary whose key is the
- /// predecessor of the specified key.
- /// </summary>
- /// <exception cref="NoSuchItemException"></exception>
- /// <param name="key">The key</param>
- /// <returns>The entry</returns>
- [Tested]
- public KeyValuePair<K, V> Predecessor(K key)
- {
- return sortedpairs.Predecessor(new KeyValuePair<K, V>(key));
- }
-
- /// <summary>
- /// Get the entry in the dictionary whose key is the
- /// successor of the specified key.
- /// </summary>
- /// <exception cref="NoSuchItemException"></exception>
- /// <param name="key">The key</param>
- /// <returns>The entry</returns>
- [Tested]
- public KeyValuePair<K, V> Successor(K key)
- {
- return sortedpairs.Successor(new KeyValuePair<K, V>(key));
- }
-
- /// <summary>
- /// Get the entry in the dictionary whose key is the
- /// weak predecessor of the specified key.
- /// </summary>
- /// <exception cref="NoSuchItemException"></exception>
- /// <param name="key">The key</param>
- /// <returns>The entry</returns>
- [Tested]
- public KeyValuePair<K, V> WeakPredecessor(K key)
- {
- return sortedpairs.WeakPredecessor(new KeyValuePair<K, V>(key));
- }
-
- /// <summary>
- /// Get the entry in the dictionary whose key is the
- /// weak successor of the specified key.
- /// </summary>
- /// <exception cref="NoSuchItemException"></exception>
- /// <param name="key">The key</param>
- /// <returns>The entry</returns>
- [Tested]
- public KeyValuePair<K, V> WeakSuccessor(K key)
- {
- return sortedpairs.WeakSuccessor(new KeyValuePair<K, V>(key));
- }
-
- #endregion
-
- #region ISortedDictionary<K,V> Members
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public KeyValuePair<K, V> FindMin()
- {
- return sortedpairs.FindMin();
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public KeyValuePair<K, V> DeleteMin()
- {
- return sortedpairs.DeleteMin();
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public KeyValuePair<K, V> FindMax()
- {
- return sortedpairs.FindMax();
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public KeyValuePair<K, V> DeleteMax()
- {
- return sortedpairs.DeleteMax();
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="cutter"></param>
- /// <param name="lowEntry"></param>
- /// <param name="lowIsValid"></param>
- /// <param name="highEntry"></param>
- /// <param name="highIsValid"></param>
- /// <returns></returns>
- public bool Cut(IComparable<K> cutter, out KeyValuePair<K, V> lowEntry, out bool lowIsValid, out KeyValuePair<K, V> highEntry, out bool highIsValid)
- {
- return sortedpairs.Cut(new KeyValuePairComparable(cutter), out lowEntry, out lowIsValid, out highEntry, out highIsValid);
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="bot"></param>
- /// <returns></returns>
- public IDirectedEnumerable<KeyValuePair<K, V>> RangeFrom(K bot)
- {
- return sortedpairs.RangeFrom(new KeyValuePair<K, V>(bot));
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="bot"></param>
- /// <param name="top"></param>
- /// <returns></returns>
- public IDirectedEnumerable<KeyValuePair<K, V>> RangeFromTo(K bot, K top)
- {
- return sortedpairs.RangeFromTo(new KeyValuePair<K, V>(bot), new KeyValuePair<K, V>(top));
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="top"></param>
- /// <returns></returns>
- public IDirectedEnumerable<KeyValuePair<K, V>> RangeTo(K top)
- {
- return sortedpairs.RangeTo(new KeyValuePair<K, V>(top));
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public IDirectedCollectionValue<KeyValuePair<K, V>> RangeAll()
- {
- return sortedpairs.RangeAll();
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="items"></param>
- public void AddSorted(SCG.IEnumerable<KeyValuePair<K, V>> items)
- {
- sortedpairs.AddSorted(items);
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="lowKey"></param>
- public void RemoveRangeFrom(K lowKey)
- {
- sortedpairs.RemoveRangeFrom(new KeyValuePair<K, V>(lowKey));
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="lowKey"></param>
- /// <param name="highKey"></param>
- public void RemoveRangeFromTo(K lowKey, K highKey)
- {
- sortedpairs.RemoveRangeFromTo(new KeyValuePair<K, V>(lowKey), new KeyValuePair<K, V>(highKey));
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="highKey"></param>
- public void RemoveRangeTo(K highKey)
- {
- sortedpairs.RemoveRangeTo(new KeyValuePair<K, V>(highKey));
- }
-
- #endregion
- [Serializable]
- class KeyValuePairComparable : IComparable<KeyValuePair<K, V>>
- {
- IComparable<K> cutter;
-
- internal KeyValuePairComparable(IComparable<K> cutter) { this.cutter = cutter; }
-
- public int CompareTo(KeyValuePair<K, V> other) { return cutter.CompareTo(other.Key); }
-
- public bool Equals(KeyValuePair<K, V> other) { return cutter.Equals(other.Key); }
- }
-
- [Serializable]
- class ProjectedDirectedEnumerable : MappedDirectedEnumerable<KeyValuePair<K, V>, K>
- {
- public ProjectedDirectedEnumerable(IDirectedEnumerable<KeyValuePair<K, V>> directedpairs) : base(directedpairs) { }
-
- public override K Map(KeyValuePair<K, V> pair) { return pair.Key; }
-
- }
-
- [Serializable]
- class ProjectedDirectedCollectionValue : MappedDirectedCollectionValue<KeyValuePair<K, V>, K>
- {
- public ProjectedDirectedCollectionValue(IDirectedCollectionValue<KeyValuePair<K, V>> directedpairs) : base(directedpairs) { }
-
- public override K Map(KeyValuePair<K, V> pair) { return pair.Key; }
-
- }
-
- [Serializable]
- class SortedKeysCollection : SequencedBase<K>, ISorted<K>
- {
- ISortedDictionary<K, V> sorteddict;
- //TODO: eliminate this. Only problem is the Find method because we lack method on dictionary that also
- // returns the actual key.
- ISorted<KeyValuePair<K, V>> sortedpairs;
- SCG.IComparer<K> comparer;
-
- internal SortedKeysCollection(ISortedDictionary<K, V> sorteddict, ISorted<KeyValuePair<K, V>> sortedpairs, SCG.IComparer<K> comparer, SCG.IEqualityComparer<K> itemequalityComparer)
- : base(itemequalityComparer)
- {
- this.sorteddict = sorteddict;
- this.sortedpairs = sortedpairs;
- this.comparer = comparer;
- }
-
- public override K Choose() { return sorteddict.Choose().Key; }
-
- public override SCG.IEnumerator<K> GetEnumerator()
- {
- foreach (KeyValuePair<K, V> p in sorteddict)
- yield return p.Key;
- }
-
- public override bool IsEmpty { get { return sorteddict.IsEmpty; } }
-
- public override int Count { [Tested]get { return sorteddict.Count; } }
-
- public override Speed CountSpeed { get { return sorteddict.CountSpeed; } }
-
- #region ISorted<K> Members
-
- public K FindMin() { return sorteddict.FindMin().Key; }
-
- public K DeleteMin() { throw new ReadOnlyCollectionException(); }
-
- public K FindMax() { return sorteddict.FindMax().Key; }
-
- public K DeleteMax() { throw new ReadOnlyCollectionException(); }
-
- public SCG.IComparer<K> Comparer { get { return comparer; } }
-
- public bool TryPredecessor(K item, out K res)
- {
- KeyValuePair<K, V> pRes;
- bool success = sorteddict.TryPredecessor(item, out pRes);
- res = pRes.Key;
- return success;
- }
-
- public bool TrySuccessor(K item, out K res)
- {
- KeyValuePair<K, V> pRes;
- bool success = sorteddict.TrySuccessor(item, out pRes);
- res = pRes.Key;
- return success;
- }
-
- public bool TryWeakPredecessor(K item, out K res)
- {
- KeyValuePair<K, V> pRes;
- bool success = sorteddict.TryWeakPredecessor(item, out pRes);
- res = pRes.Key;
- return success;
- }
-
- public bool TryWeakSuccessor(K item, out K res)
- {
- KeyValuePair<K, V> pRes;
- bool success = sorteddict.TryWeakSuccessor(item, out pRes);
- res = pRes.Key;
- return success;
- }
-
- public K Predecessor(K item) { return sorteddict.Predecessor(item).Key; }
-
- public K Successor(K item) { return sorteddict.Successor(item).Key; }
-
- public K WeakPredecessor(K item) { return sorteddict.WeakPredecessor(item).Key; }
-
- public K WeakSuccessor(K item) { return sorteddict.WeakSuccessor(item).Key; }
-
- public bool Cut(IComparable<K> c, out K low, out bool lowIsValid, out K high, out bool highIsValid)
- {
- KeyValuePair<K, V> lowpair, highpair;
- bool retval = sorteddict.Cut(c, out lowpair, out lowIsValid, out highpair, out highIsValid);
- low = lowpair.Key;
- high = highpair.Key;
- return retval;
- }
-
- public IDirectedEnumerable<K> RangeFrom(K bot)
- {
- return new ProjectedDirectedEnumerable(sorteddict.RangeFrom(bot));
- }
-
- public IDirectedEnumerable<K> RangeFromTo(K bot, K top)
- {
- return new ProjectedDirectedEnumerable(sorteddict.RangeFromTo(bot, top));
- }
-
- public IDirectedEnumerable<K> RangeTo(K top)
- {
- return new ProjectedDirectedEnumerable(sorteddict.RangeTo(top));
- }
-
- public IDirectedCollectionValue<K> RangeAll()
- {
- return new ProjectedDirectedCollectionValue(sorteddict.RangeAll());
- }
-
- public void AddSorted<U>(SCG.IEnumerable<U> items) where U : K { throw new ReadOnlyCollectionException(); }
-
- public void RemoveRangeFrom(K low) { throw new ReadOnlyCollectionException(); }
-
- public void RemoveRangeFromTo(K low, K hi) { throw new ReadOnlyCollectionException(); }
-
- public void RemoveRangeTo(K hi) { throw new ReadOnlyCollectionException(); }
- #endregion
-
- #region ICollection<K> Members
- public Speed ContainsSpeed { get { return sorteddict.ContainsSpeed; } }
-
- public bool Contains(K key) { return sorteddict.Contains(key); ; }
-
- public int ContainsCount(K item) { return sorteddict.Contains(item) ? 1 : 0; }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public virtual ICollectionValue<K> UniqueItems()
- {
- return this;
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public virtual ICollectionValue<KeyValuePair<K, int>> ItemMultiplicities()
- {
- return new MultiplicityOne<K>(this);
- }
-
-
- public bool ContainsAll<U>(SCG.IEnumerable<U> items) where U : K
- {
- //TODO: optimize?
- foreach (K item in items)
- if (!sorteddict.Contains(item))
- return false;
- return true;
- }
-
- public bool Find(ref K item)
- {
- KeyValuePair<K, V> p = new KeyValuePair<K, V>(item);
- bool retval = sortedpairs.Find(ref p);
- item = p.Key;
- return retval;
- }
-
- public bool FindOrAdd(ref K item) { throw new ReadOnlyCollectionException(); }
-
- public bool Update(K item) { throw new ReadOnlyCollectionException(); }
-
- public bool Update(K item, out K olditem) { throw new ReadOnlyCollectionException(); }
-
- public bool UpdateOrAdd(K item) { throw new ReadOnlyCollectionException(); }
-
- public bool UpdateOrAdd(K item, out K olditem) { throw new ReadOnlyCollectionException(); }
-
- public bool Remove(K item) { throw new ReadOnlyCollectionException(); }
-
- public bool Remove(K item, out K removeditem) { throw new ReadOnlyCollectionException(); }
-
- public void RemoveAllCopies(K item) { throw new ReadOnlyCollectionException(); }
-
- public void RemoveAll<U>(SCG.IEnumerable<U> items) where U : K { throw new ReadOnlyCollectionException(); }
-
- public void Clear() { throw new ReadOnlyCollectionException(); }
-
- public void RetainAll<U>(SCG.IEnumerable<U> items) where U : K { throw new ReadOnlyCollectionException(); }
-
- #endregion
-
- #region IExtensible<K> Members
- public override bool IsReadOnly { get { return true; } }
-
- public bool AllowsDuplicates { get { return false; } }
-
- public bool DuplicatesByCounting { get { return true; } }
-
- public bool Add(K item) { throw new ReadOnlyCollectionException(); }
-
- void SCG.ICollection<K>.Add(K item) { throw new ReadOnlyCollectionException(); }
-
- public void AddAll(System.Collections.Generic.IEnumerable<K> items) { throw new ReadOnlyCollectionException(); }
-
- public void AddAll<U>(System.Collections.Generic.IEnumerable<U> items) where U : K { throw new ReadOnlyCollectionException(); }
-
- public bool Check() { return sorteddict.Check(); }
-
- #endregion
-
- #region IDirectedCollectionValue<K> Members
-
- public override IDirectedCollectionValue<K> Backwards()
- {
- return RangeAll().Backwards();
- }
-
- #endregion
-
- #region IDirectedEnumerable<K> Members
-
- IDirectedEnumerable<K> IDirectedEnumerable<K>.Backwards() { return Backwards(); }
- #endregion
-
- #region ICloneable Members
-
- //TODO: test
- /// <summary>
- /// Make a shallow copy of this SortedKeysCollection.
- /// </summary>
- /// <returns></returns>
- public virtual object Clone()
- {
- //
- SortedArrayDictionary<K, V> dictclone = new SortedArrayDictionary<K, V>(sortedpairs.Count, comparer, EqualityComparer);
- SortedArray<KeyValuePair<K, V>> sortedpairsclone = (SortedArray<KeyValuePair<K, V>>)(dictclone.sortedpairs);
- foreach (K key in sorteddict.Keys)
- {
- sortedpairsclone.Add(new KeyValuePair<K, V>(key, default(V)));
- }
- return new SortedKeysCollection(dictclone, sortedpairsclone, comparer, EqualityComparer);
- }
-
- #endregion
-
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="stringbuilder"></param>
- /// <param name="rest"></param>
- /// <param name="formatProvider"></param>
- /// <returns></returns>
- public override bool Show(System.Text.StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider)
- {
- return Showing.ShowDictionary<K, V>(this, stringbuilder, ref rest, formatProvider);
- }
-
- }
-
- [Serializable]
- class SortedArrayDictionary<K, V> : SortedDictionaryBase<K, V>, IDictionary<K, V>, ISortedDictionary<K, V>
- {
- #region Constructors
-
- public SortedArrayDictionary() : this(Comparer<K>.Default, EqualityComparer<K>.Default) { }
-
- /// <summary>
- /// Create a red-black tree dictionary using an external comparer for keys.
- /// </summary>
- /// <param name="comparer">The external comparer</param>
- public SortedArrayDictionary(SCG.IComparer<K> comparer) : this(comparer, new ComparerZeroHashCodeEqualityComparer<K>(comparer)) { }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="comparer"></param>
- /// <param name="equalityComparer"></param>
- public SortedArrayDictionary(SCG.IComparer<K> comparer, SCG.IEqualityComparer<K> equalityComparer)
- : base(comparer, equalityComparer)
- {
- pairs = sortedpairs = new SortedArray<KeyValuePair<K, V>>(new KeyValuePairComparer<K, V>(comparer));
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="comparer"></param>
- /// <param name="equalityComparer"></param>
- /// <param name="capacity"></param>
- public SortedArrayDictionary(int capacity, SCG.IComparer<K> comparer, SCG.IEqualityComparer<K> equalityComparer)
- : base(comparer, equalityComparer)
- {
- pairs = sortedpairs = new SortedArray<KeyValuePair<K, V>>(capacity, new KeyValuePairComparer<K, V>(comparer));
- }
- #endregion
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public override object Clone()
- {
- SortedArrayDictionary<K, V> clone = new SortedArrayDictionary<K, V>(Comparer, EqualityComparer);
- clone.sortedpairs.AddSorted(sortedpairs);
- return clone;
- }
-
- }
+/* + Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +using System; +using System.Diagnostics; +using SCG = System.Collections.Generic; +namespace C5 +{ + /// <summary> + /// An entry in a dictionary from K to V. + /// </summary> + [Serializable] + public struct KeyValuePair<K, V> : IEquatable<KeyValuePair<K, V>>, IShowable + { + /// <summary> + /// The key field of the entry + /// </summary> + public K Key; + + /// <summary> + /// The value field of the entry + /// </summary> + public V Value; + + /// <summary> + /// Create an entry with specified key and value + /// </summary> + /// <param name="key">The key</param> + /// <param name="value">The value</param> + public KeyValuePair(K key, V value) { Key = key; Value = value; } + + + /// <summary> + /// Create an entry with a specified key. The value will be the default value of type <code>V</code>. + /// </summary> + /// <param name="key">The key</param> + public KeyValuePair(K key) { Key = key; Value = default(V); } + + + /// <summary> + /// Pretty print an entry + /// </summary> + /// <returns>(key, value)</returns> + [Tested] + public override string ToString() { return "(" + Key + ", " + Value + ")"; } + + + /// <summary> + /// Check equality of entries. + /// </summary> + /// <param name="obj">The other object</param> + /// <returns>True if obj is an entry of the same type and has the same key and value</returns> + [Tested] + public override bool Equals(object obj) + { + if (!(obj is KeyValuePair<K, V>)) + return false; + KeyValuePair<K, V> other = (KeyValuePair<K, V>)obj; + return Equals(other); + } + + + /// <summary> + /// Get the hash code of the pair. + /// </summary> + /// <returns>The hash code</returns> + [Tested] + public override int GetHashCode() { return EqualityComparer<K>.Default.GetHashCode(Key) + 13984681 * EqualityComparer<V>.Default.GetHashCode(Value); } + + /// <summary> + /// + /// </summary> + /// <param name="other"></param> + /// <returns></returns> + public bool Equals(KeyValuePair<K, V> other) + { + return EqualityComparer<K>.Default.Equals(Key, other.Key) && EqualityComparer<V>.Default.Equals(Value, other.Value); + } + + /// <summary> + /// + /// </summary> + /// <param name="pair1"></param> + /// <param name="pair2"></param> + /// <returns></returns> + public static bool operator ==(KeyValuePair<K, V> pair1, KeyValuePair<K, V> pair2) { return pair1.Equals(pair2); } + + /// <summary> + /// + /// </summary> + /// <param name="pair1"></param> + /// <param name="pair2"></param> + /// <returns></returns> + public static bool operator !=(KeyValuePair<K, V> pair1, KeyValuePair<K, V> pair2) { return !pair1.Equals(pair2); } + + #region IShowable Members + + /// <summary> + /// + /// </summary> + /// <param name="stringbuilder"></param> + /// <param name="formatProvider"></param> + /// <param name="rest"></param> + /// <returns></returns> + public bool Show(System.Text.StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider) + { + if (rest < 0) + return false; + if (!Showing.Show(Key, stringbuilder, ref rest, formatProvider)) + return false; + stringbuilder.Append(" => "); + rest -= 4; + if (!Showing.Show(Value, stringbuilder, ref rest, formatProvider)) + return false; + return rest >= 0; + } + #endregion + + #region IFormattable Members + + /// <summary> + /// + /// </summary> + /// <param name="format"></param> + /// <param name="formatProvider"></param> + /// <returns></returns> + public string ToString(string format, IFormatProvider formatProvider) + { + return Showing.ShowString(this, format, formatProvider); + } + + #endregion + } + + + + /// <summary> + /// Default comparer for dictionary entries in a sorted dictionary. + /// Entry comparisons only look at keys and uses an externally defined comparer for that. + /// </summary> + [Serializable] + public class KeyValuePairComparer<K, V> : SCG.IComparer<KeyValuePair<K, V>> + { + SCG.IComparer<K> comparer; + + + /// <summary> + /// Create an entry comparer for a item comparer of the keys + /// </summary> + /// <param name="comparer">Comparer of keys</param> + public KeyValuePairComparer(SCG.IComparer<K> comparer) + { + if (comparer == null) + throw new NullReferenceException(); + this.comparer = comparer; + } + + + /// <summary> + /// Compare two entries + /// </summary> + /// <param name="entry1">First entry</param> + /// <param name="entry2">Second entry</param> + /// <returns>The result of comparing the keys</returns> + [Tested] + public int Compare(KeyValuePair<K, V> entry1, KeyValuePair<K, V> entry2) + { return comparer.Compare(entry1.Key, entry2.Key); } + } + + + + /// <summary> + /// Default equalityComparer for dictionary entries. + /// Operations only look at keys and uses an externaly defined equalityComparer for that. + /// </summary> + [Serializable] + public sealed class KeyValuePairEqualityComparer<K, V> : SCG.IEqualityComparer<KeyValuePair<K, V>> + { + SCG.IEqualityComparer<K> keyequalityComparer; + + + /// <summary> + /// Create an entry equalityComparer using the default equalityComparer for keys + /// </summary> + public KeyValuePairEqualityComparer() { keyequalityComparer = EqualityComparer<K>.Default; } + + + /// <summary> + /// Create an entry equalityComparer from a specified item equalityComparer for the keys + /// </summary> + /// <param name="keyequalityComparer">The key equalityComparer</param> + public KeyValuePairEqualityComparer(SCG.IEqualityComparer<K> keyequalityComparer) + { + if (keyequalityComparer == null) + throw new NullReferenceException("Key equality comparer cannot be null"); + this.keyequalityComparer = keyequalityComparer; + } + + + /// <summary> + /// Get the hash code of the entry + /// </summary> + /// <param name="entry">The entry</param> + /// <returns>The hash code of the key</returns> + [Tested] + public int GetHashCode(KeyValuePair<K, V> entry) { return keyequalityComparer.GetHashCode(entry.Key); } + + + /// <summary> + /// Test two entries for equality + /// </summary> + /// <param name="entry1">First entry</param> + /// <param name="entry2">Second entry</param> + /// <returns>True if keys are equal</returns> + [Tested] + public bool Equals(KeyValuePair<K, V> entry1, KeyValuePair<K, V> entry2) + { return keyequalityComparer.Equals(entry1.Key, entry2.Key); } + } + + + + /// <summary> + /// A base class for implementing a dictionary based on a set collection implementation. + /// <i>See the source code for <see cref="T:C5.HashDictionary`2"/> for an example</i> + /// + /// </summary> + [Serializable] + public abstract class DictionaryBase<K, V> : CollectionValueBase<KeyValuePair<K, V>>, IDictionary<K, V> + { + /// <summary> + /// The set collection of entries underlying this dictionary implementation + /// </summary> + protected ICollection<KeyValuePair<K, V>> pairs; + + SCG.IEqualityComparer<K> keyequalityComparer; + + #region Events + ProxyEventBlock<KeyValuePair<K, V>> eventBlock; + + /// <summary> + /// The change event. Will be raised for every change operation on the collection. + /// </summary> + public override event CollectionChangedHandler<KeyValuePair<K, V>> CollectionChanged + { + add { (eventBlock ?? (eventBlock = new ProxyEventBlock<KeyValuePair<K, V>>(this, pairs))).CollectionChanged += value; } + remove { if (eventBlock != null) eventBlock.CollectionChanged -= value; } + } + + /// <summary> + /// The change event. Will be raised for every change operation on the collection. + /// </summary> + public override event CollectionClearedHandler<KeyValuePair<K, V>> CollectionCleared + { + add { (eventBlock ?? (eventBlock = new ProxyEventBlock<KeyValuePair<K, V>>(this, pairs))).CollectionCleared += value; } + remove { if (eventBlock != null) eventBlock.CollectionCleared -= value; } + } + + /// <summary> + /// The item added event. Will be raised for every individual addition to the collection. + /// </summary> + public override event ItemsAddedHandler<KeyValuePair<K, V>> ItemsAdded + { + add { (eventBlock ?? (eventBlock = new ProxyEventBlock<KeyValuePair<K, V>>(this, pairs))).ItemsAdded += value; } + remove { if (eventBlock != null) eventBlock.ItemsAdded -= value; } + } + + /// <summary> + /// The item added event. Will be raised for every individual removal from the collection. + /// </summary> + public override event ItemsRemovedHandler<KeyValuePair<K, V>> ItemsRemoved + { + add { (eventBlock ?? (eventBlock = new ProxyEventBlock<KeyValuePair<K, V>>(this, pairs))).ItemsRemoved += value; } + remove { if (eventBlock != null) eventBlock.ItemsRemoved -= value; } + } + + /// <summary> + /// + /// </summary> + public override EventTypeEnum ListenableEvents + { + get + { + return EventTypeEnum.Basic; + } + } + + /// <summary> + /// + /// </summary> + public override EventTypeEnum ActiveEvents + { + get + { + return pairs.ActiveEvents; + } + } + + #endregion + + /// <summary> + /// + /// </summary> + /// <param name="keyequalityComparer"></param> + protected DictionaryBase(SCG.IEqualityComparer<K> keyequalityComparer) + { + if (keyequalityComparer == null) + throw new NullReferenceException("Key equality comparer cannot be null"); + this.keyequalityComparer = keyequalityComparer; + } + + #region IDictionary<K,V> Members + + /// <summary> + /// + /// </summary> + /// <value></value> + public virtual SCG.IEqualityComparer<K> EqualityComparer { get { return keyequalityComparer; } } + + + /// <summary> + /// Add a new (key, value) pair (a mapping) to the dictionary. + /// </summary> + /// <exception cref="DuplicateNotAllowedException"> if there already is an entry with the same key. </exception> + /// <param name="key">Key to add</param> + /// <param name="value">Value to add</param> + [Tested] + public virtual void Add(K key, V value) + { + KeyValuePair<K, V> p = new KeyValuePair<K, V>(key, value); + + if (!pairs.Add(p)) + throw new DuplicateNotAllowedException("Key being added: '" + key + "'"); + } + + /// <summary> + /// Add the entries from a collection of <see cref="T:C5.KeyValuePair`2"/> pairs to this dictionary. + /// <para><b>TODO: add restrictions L:K and W:V when the .Net SDK allows it </b></para> + /// </summary> + /// <exception cref="DuplicateNotAllowedException"> + /// If the input contains duplicate keys or a key already present in this dictionary.</exception> + /// <param name="entries"></param> + public virtual void AddAll<L, W>(SCG.IEnumerable<KeyValuePair<L, W>> entries) + where L : K + where W : V + { + foreach (KeyValuePair<L, W> pair in entries) + { + KeyValuePair<K, V> p = new KeyValuePair<K, V>(pair.Key, pair.Value); + if (!pairs.Add(p)) + throw new DuplicateNotAllowedException("Key being added: '" + pair.Key + "'"); + } + } + + /// <summary> + /// Remove an entry with a given key from the dictionary + /// </summary> + /// <param name="key">The key of the entry to remove</param> + /// <returns>True if an entry was found (and removed)</returns> + [Tested] + public virtual bool Remove(K key) + { + KeyValuePair<K, V> p = new KeyValuePair<K, V>(key); + + return pairs.Remove(p); + } + + + /// <summary> + /// Remove an entry with a given key from the dictionary and report its value. + /// </summary> + /// <param name="key">The key of the entry to remove</param> + /// <param name="value">On exit, the value of the removed entry</param> + /// <returns>True if an entry was found (and removed)</returns> + [Tested] + public virtual bool Remove(K key, out V value) + { + KeyValuePair<K, V> p = new KeyValuePair<K, V>(key); + + if (pairs.Remove(p, out p)) + { + value = p.Value; + return true; + } + else + { + value = default(V); + return false; + } + } + + + /// <summary> + /// Remove all entries from the dictionary + /// </summary> + [Tested] + public virtual void Clear() { pairs.Clear(); } + + /// <summary> + /// + /// </summary> + /// <value></value> + public virtual Speed ContainsSpeed { get { return pairs.ContainsSpeed; } } + + /// <summary> + /// Check if there is an entry with a specified key + /// </summary> + /// <param name="key">The key to look for</param> + /// <returns>True if key was found</returns> + [Tested] + public virtual bool Contains(K key) + { + KeyValuePair<K, V> p = new KeyValuePair<K, V>(key); + + return pairs.Contains(p); + } + + [Serializable] + class LiftedEnumerable<H> : SCG.IEnumerable<KeyValuePair<K, V>> where H : K + { + SCG.IEnumerable<H> keys; + public LiftedEnumerable(SCG.IEnumerable<H> keys) { this.keys = keys; } + public SCG.IEnumerator<KeyValuePair<K, V>> GetEnumerator() { foreach (H key in keys) yield return new KeyValuePair<K, V>(key); } + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + throw new Exception("The method or operation is not implemented."); + } + + #endregion + } + + /// <summary> + /// + /// </summary> + /// <param name="keys"></param> + /// <returns></returns> + public virtual bool ContainsAll<H>(SCG.IEnumerable<H> keys) where H : K + { + return pairs.ContainsAll(new LiftedEnumerable<H>(keys)); + } + + /// <summary> + /// Check if there is an entry with a specified key and report the corresponding + /// value if found. This can be seen as a safe form of "val = this[key]". + /// </summary> + /// <param name="key">The key to look for</param> + /// <param name="value">On exit, the value of the entry</param> + /// <returns>True if key was found</returns> + [Tested] + public virtual bool Find(K key, out V value) + { + return Find(ref key, out value); + } + /// <summary> + /// Check if there is an entry with a specified key and report the corresponding + /// value if found. This can be seen as a safe form of "val = this[key]". + /// </summary> + /// <param name="key">The key to look for</param> + /// <param name="value">On exit, the value of the entry</param> + /// <returns>True if key was found</returns> + public virtual bool Find(ref K key, out V value) + { + KeyValuePair<K, V> p = new KeyValuePair<K, V>(key); + + if (pairs.Find(ref p)) + { + key = p.Key; + value = p.Value; + return true; + } + else + { + value = default(V); + return false; + } + } + + + /// <summary> + /// Look for a specific key in the dictionary and if found replace the value with a new one. + /// This can be seen as a non-adding version of "this[key] = val". + /// </summary> + /// <param name="key">The key to look for</param> + /// <param name="value">The new value</param> + /// <returns>True if key was found</returns> + [Tested] + public virtual bool Update(K key, V value) + { + KeyValuePair<K, V> p = new KeyValuePair<K, V>(key, value); + + return pairs.Update(p); + } + + + /// <summary> + /// + /// </summary> + /// <param name="key"></param> + /// <param name="value"></param> + /// <param name="oldvalue"></param> + /// <returns></returns> + public virtual bool Update(K key, V value, out V oldvalue) + { + KeyValuePair<K, V> p = new KeyValuePair<K, V>(key, value); + + bool retval = pairs.Update(p, out p); + oldvalue = p.Value; + return retval; + } + + + /// <summary> + /// Look for a specific key in the dictionary. If found, report the corresponding value, + /// else add an entry with the key and the supplied value. + /// </summary> + /// <param name="key">On entry the key to look for</param> + /// <param name="value">On entry the value to add if the key is not found. + /// On exit the value found if any.</param> + /// <returns>True if key was found</returns> + [Tested] + public virtual bool FindOrAdd(K key, ref V value) + { + KeyValuePair<K, V> p = new KeyValuePair<K, V>(key, value); + + if (!pairs.FindOrAdd(ref p)) + return false; + else + { + value = p.Value; + //key = p.key; + return true; + } + } + + + /// <summary> + /// Update value in dictionary corresponding to key if found, else add new entry. + /// More general than "this[key] = val;" by reporting if key was found. + /// </summary> + /// <param name="key">The key to look for</param> + /// <param name="value">The value to add or replace with.</param> + /// <returns>True if entry was updated.</returns> + [Tested] + public virtual bool UpdateOrAdd(K key, V value) + { + return pairs.UpdateOrAdd(new KeyValuePair<K, V>(key, value)); + } + + + /// <summary> + /// Update value in dictionary corresponding to key if found, else add new entry. + /// More general than "this[key] = val;" by reporting if key was found and the old value if any. + /// </summary> + /// <param name="key"></param> + /// <param name="value"></param> + /// <param name="oldvalue"></param> + /// <returns></returns> + public virtual bool UpdateOrAdd(K key, V value, out V oldvalue) + { + KeyValuePair<K, V> p = new KeyValuePair<K, V>(key, value); + bool retval = pairs.UpdateOrAdd(p, out p); + oldvalue = p.Value; + return retval; + } + + + + #region Keys,Values support classes + [Serializable] + internal class ValuesCollection : CollectionValueBase<V>, ICollectionValue<V> + { + ICollection<KeyValuePair<K, V>> pairs; + + + internal ValuesCollection(ICollection<KeyValuePair<K, V>> pairs) + { this.pairs = pairs; } + + + public override V Choose() { return pairs.Choose().Value; } + + [Tested] + public override SCG.IEnumerator<V> GetEnumerator() + { + //Updatecheck is performed by the pairs enumerator + foreach (KeyValuePair<K, V> p in pairs) + yield return p.Value; + } + + public override bool IsEmpty { get { return pairs.IsEmpty; } } + + [Tested] + public override int Count { [Tested]get { return pairs.Count; } } + + public override Speed CountSpeed { get { return Speed.Constant; } } + } + + + + [Serializable] + internal class KeysCollection : CollectionValueBase<K>, ICollectionValue<K> + { + ICollection<KeyValuePair<K, V>> pairs; + + + internal KeysCollection(ICollection<KeyValuePair<K, V>> pairs) + { this.pairs = pairs; } + + public override K Choose() { return pairs.Choose().Key; } + + [Tested] + public override SCG.IEnumerator<K> GetEnumerator() + { + foreach (KeyValuePair<K, V> p in pairs) + yield return p.Key; + } + + public override bool IsEmpty { get { return pairs.IsEmpty; } } + + [Tested] + public override int Count { [Tested]get { return pairs.Count; } } + + public override Speed CountSpeed { get { return pairs.CountSpeed; } } + } + #endregion + + /// <summary> + /// + /// </summary> + /// <value>A collection containg the all the keys of the dictionary</value> + [Tested] + public virtual ICollectionValue<K> Keys { [Tested]get { return new KeysCollection(pairs); } } + + + /// <summary> + /// + /// </summary> + /// <value>A collection containing all the values of the dictionary</value> + [Tested] + public virtual ICollectionValue<V> Values { [Tested]get { return new ValuesCollection(pairs); } } + + /// <summary> + /// + /// </summary> + public virtual Fun<K, V> Fun { get { return delegate(K k) { return this[k]; }; } } + + /// <summary> + /// Indexer by key for dictionary. + /// <para>The get method will throw an exception if no entry is found. </para> + /// <para>The set method behaves like <see cref="M:C5.DictionaryBase`2.UpdateOrAdd(`0,`1)"/>.</para> + /// </summary> + /// <exception cref="NoSuchItemException"> On get if no entry is found. </exception> + /// <value>The value corresponding to the key</value> + [Tested] + public virtual V this[K key] + { + [Tested] + get + { + KeyValuePair<K, V> p = new KeyValuePair<K, V>(key); + + if (pairs.Find(ref p)) + return p.Value; + else + throw new NoSuchItemException("Key '" + key.ToString() + "' not present in Dictionary"); + } + [Tested] + set + { pairs.UpdateOrAdd(new KeyValuePair<K, V>(key, value)); } + } + + + /// <summary> + /// + /// </summary> + /// <value>True if dictionary is read only</value> + [Tested] + public virtual bool IsReadOnly { [Tested]get { return pairs.IsReadOnly; } } + + + /// <summary> + /// Check the integrity of the internal data structures of this dictionary. + /// </summary> + /// <returns>True if check does not fail.</returns> + [Tested] + public virtual bool Check() { return pairs.Check(); } + + #endregion + + #region ICollectionValue<KeyValuePair<K,V>> Members + + /// <summary> + /// + /// </summary> + /// <value>True if this collection is empty.</value> + public override bool IsEmpty { get { return pairs.IsEmpty; } } + + + /// <summary> + /// + /// </summary> + /// <value>The number of entrues in the dictionary</value> + [Tested] + public override int Count { [Tested]get { return pairs.Count; } } + + /// <summary> + /// + /// </summary> + /// <value>The number of entrues in the dictionary</value> + [Tested] + public override Speed CountSpeed { [Tested]get { return pairs.CountSpeed; } } + + /// <summary> + /// Choose some entry in this Dictionary. + /// </summary> + /// <exception cref="NoSuchItemException">if collection is empty.</exception> + /// <returns></returns> + public override KeyValuePair<K, V> Choose() { return pairs.Choose(); } + + /// <summary> + /// Create an enumerator for the collection of entries of the dictionary + /// </summary> + /// <returns>The enumerator</returns> + [Tested] + public override SCG.IEnumerator<KeyValuePair<K, V>> GetEnumerator() + { + return pairs.GetEnumerator(); ; + } + + #endregion + + /// <summary> + /// + /// </summary> + /// <param name="stringbuilder"></param> + /// <param name="rest"></param> + /// <param name="formatProvider"></param> + /// <returns></returns> + public override bool Show(System.Text.StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider) + { + return Showing.ShowDictionary<K, V>(this, stringbuilder, ref rest, formatProvider); + } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public abstract object Clone(); + + } + + /// <summary> + /// A base class for implementing a sorted dictionary based on a sorted set collection implementation. + /// <i>See the source code for <see cref="T:C5.TreeDictionary`2"/> for an example</i> + /// + /// </summary> + [Serializable] + public abstract class SortedDictionaryBase<K, V> : DictionaryBase<K, V>, ISortedDictionary<K, V> + { + #region Fields + + /// <summary> + /// + /// </summary> + protected ISorted<KeyValuePair<K, V>> sortedpairs; + SCG.IComparer<K> keycomparer; + + /// <summary> + /// + /// </summary> + /// <param name="keycomparer"></param> + /// <param name="keyequalityComparer"></param> + protected SortedDictionaryBase(SCG.IComparer<K> keycomparer, SCG.IEqualityComparer<K> keyequalityComparer) : base(keyequalityComparer) { this.keycomparer = keycomparer; } + + #endregion + + #region ISortedDictionary<K,V> Members + + /// <summary> + /// The key comparer used by this dictionary. + /// </summary> + /// <value></value> + public SCG.IComparer<K> Comparer { get { return keycomparer; } } + + /// <summary> + /// + /// </summary> + /// <value></value> + public new ISorted<K> Keys { get { return new SortedKeysCollection(this, sortedpairs, keycomparer, EqualityComparer); } } + + /// <summary> + /// Find the entry in the dictionary whose key is the + /// predecessor of the specified key. + /// </summary> + /// <param name="key">The key</param> + /// <param name="res">The predecessor, if any</param> + /// <returns>True if key has a predecessor</returns> + [Tested] + public bool TryPredecessor(K key, out KeyValuePair<K, V> res) + { + return sortedpairs.TryPredecessor(new KeyValuePair<K, V>(key), out res); + } + + /// <summary> + /// Find the entry in the dictionary whose key is the + /// successor of the specified key. + /// </summary> + /// <param name="key">The key</param> + /// <param name="res">The successor, if any</param> + /// <returns>True if the key has a successor</returns> + [Tested] + public bool TrySuccessor(K key, out KeyValuePair<K, V> res) + { + return sortedpairs.TrySuccessor(new KeyValuePair<K, V>(key), out res); + } + + /// <summary> + /// Find the entry in the dictionary whose key is the + /// weak predecessor of the specified key. + /// </summary> + /// <param name="key">The key</param> + /// <param name="res">The predecessor, if any</param> + /// <returns>True if key has a weak predecessor</returns> + [Tested] + public bool TryWeakPredecessor(K key, out KeyValuePair<K, V> res) + { + return sortedpairs.TryWeakPredecessor(new KeyValuePair<K, V>(key), out res); + } + + /// <summary> + /// Find the entry in the dictionary whose key is the + /// weak successor of the specified key. + /// </summary> + /// <param name="key">The key</param> + /// <param name="res">The weak successor, if any</param> + /// <returns>True if the key has a weak successor</returns> + [Tested] + public bool TryWeakSuccessor(K key, out KeyValuePair<K, V> res) + { + return sortedpairs.TryWeakSuccessor(new KeyValuePair<K, V>(key), out res); + } + + /// <summary> + /// Get the entry in the dictionary whose key is the + /// predecessor of the specified key. + /// </summary> + /// <exception cref="NoSuchItemException"></exception> + /// <param name="key">The key</param> + /// <returns>The entry</returns> + [Tested] + public KeyValuePair<K, V> Predecessor(K key) + { + return sortedpairs.Predecessor(new KeyValuePair<K, V>(key)); + } + + /// <summary> + /// Get the entry in the dictionary whose key is the + /// successor of the specified key. + /// </summary> + /// <exception cref="NoSuchItemException"></exception> + /// <param name="key">The key</param> + /// <returns>The entry</returns> + [Tested] + public KeyValuePair<K, V> Successor(K key) + { + return sortedpairs.Successor(new KeyValuePair<K, V>(key)); + } + + /// <summary> + /// Get the entry in the dictionary whose key is the + /// weak predecessor of the specified key. + /// </summary> + /// <exception cref="NoSuchItemException"></exception> + /// <param name="key">The key</param> + /// <returns>The entry</returns> + [Tested] + public KeyValuePair<K, V> WeakPredecessor(K key) + { + return sortedpairs.WeakPredecessor(new KeyValuePair<K, V>(key)); + } + + /// <summary> + /// Get the entry in the dictionary whose key is the + /// weak successor of the specified key. + /// </summary> + /// <exception cref="NoSuchItemException"></exception> + /// <param name="key">The key</param> + /// <returns>The entry</returns> + [Tested] + public KeyValuePair<K, V> WeakSuccessor(K key) + { + return sortedpairs.WeakSuccessor(new KeyValuePair<K, V>(key)); + } + + #endregion + + #region ISortedDictionary<K,V> Members + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public KeyValuePair<K, V> FindMin() + { + return sortedpairs.FindMin(); + } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public KeyValuePair<K, V> DeleteMin() + { + return sortedpairs.DeleteMin(); + } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public KeyValuePair<K, V> FindMax() + { + return sortedpairs.FindMax(); + } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public KeyValuePair<K, V> DeleteMax() + { + return sortedpairs.DeleteMax(); + } + + /// <summary> + /// + /// </summary> + /// <param name="cutter"></param> + /// <param name="lowEntry"></param> + /// <param name="lowIsValid"></param> + /// <param name="highEntry"></param> + /// <param name="highIsValid"></param> + /// <returns></returns> + public bool Cut(IComparable<K> cutter, out KeyValuePair<K, V> lowEntry, out bool lowIsValid, out KeyValuePair<K, V> highEntry, out bool highIsValid) + { + return sortedpairs.Cut(new KeyValuePairComparable(cutter), out lowEntry, out lowIsValid, out highEntry, out highIsValid); + } + + /// <summary> + /// + /// </summary> + /// <param name="bot"></param> + /// <returns></returns> + public IDirectedEnumerable<KeyValuePair<K, V>> RangeFrom(K bot) + { + return sortedpairs.RangeFrom(new KeyValuePair<K, V>(bot)); + } + + /// <summary> + /// + /// </summary> + /// <param name="bot"></param> + /// <param name="top"></param> + /// <returns></returns> + public IDirectedEnumerable<KeyValuePair<K, V>> RangeFromTo(K bot, K top) + { + return sortedpairs.RangeFromTo(new KeyValuePair<K, V>(bot), new KeyValuePair<K, V>(top)); + } + + /// <summary> + /// + /// </summary> + /// <param name="top"></param> + /// <returns></returns> + public IDirectedEnumerable<KeyValuePair<K, V>> RangeTo(K top) + { + return sortedpairs.RangeTo(new KeyValuePair<K, V>(top)); + } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public IDirectedCollectionValue<KeyValuePair<K, V>> RangeAll() + { + return sortedpairs.RangeAll(); + } + + /// <summary> + /// + /// </summary> + /// <param name="items"></param> + public void AddSorted(SCG.IEnumerable<KeyValuePair<K, V>> items) + { + sortedpairs.AddSorted(items); + } + + /// <summary> + /// + /// </summary> + /// <param name="lowKey"></param> + public void RemoveRangeFrom(K lowKey) + { + sortedpairs.RemoveRangeFrom(new KeyValuePair<K, V>(lowKey)); + } + + /// <summary> + /// + /// </summary> + /// <param name="lowKey"></param> + /// <param name="highKey"></param> + public void RemoveRangeFromTo(K lowKey, K highKey) + { + sortedpairs.RemoveRangeFromTo(new KeyValuePair<K, V>(lowKey), new KeyValuePair<K, V>(highKey)); + } + + /// <summary> + /// + /// </summary> + /// <param name="highKey"></param> + public void RemoveRangeTo(K highKey) + { + sortedpairs.RemoveRangeTo(new KeyValuePair<K, V>(highKey)); + } + + #endregion + [Serializable] + class KeyValuePairComparable : IComparable<KeyValuePair<K, V>> + { + IComparable<K> cutter; + + internal KeyValuePairComparable(IComparable<K> cutter) { this.cutter = cutter; } + + public int CompareTo(KeyValuePair<K, V> other) { return cutter.CompareTo(other.Key); } + + public bool Equals(KeyValuePair<K, V> other) { return cutter.Equals(other.Key); } + } + + [Serializable] + class ProjectedDirectedEnumerable : MappedDirectedEnumerable<KeyValuePair<K, V>, K> + { + public ProjectedDirectedEnumerable(IDirectedEnumerable<KeyValuePair<K, V>> directedpairs) : base(directedpairs) { } + + public override K Map(KeyValuePair<K, V> pair) { return pair.Key; } + + } + + [Serializable] + class ProjectedDirectedCollectionValue : MappedDirectedCollectionValue<KeyValuePair<K, V>, K> + { + public ProjectedDirectedCollectionValue(IDirectedCollectionValue<KeyValuePair<K, V>> directedpairs) : base(directedpairs) { } + + public override K Map(KeyValuePair<K, V> pair) { return pair.Key; } + + } + + [Serializable] + class SortedKeysCollection : SequencedBase<K>, ISorted<K> + { + ISortedDictionary<K, V> sorteddict; + //TODO: eliminate this. Only problem is the Find method because we lack method on dictionary that also + // returns the actual key. + ISorted<KeyValuePair<K, V>> sortedpairs; + SCG.IComparer<K> comparer; + + internal SortedKeysCollection(ISortedDictionary<K, V> sorteddict, ISorted<KeyValuePair<K, V>> sortedpairs, SCG.IComparer<K> comparer, SCG.IEqualityComparer<K> itemequalityComparer) + : base(itemequalityComparer) + { + this.sorteddict = sorteddict; + this.sortedpairs = sortedpairs; + this.comparer = comparer; + } + + public override K Choose() { return sorteddict.Choose().Key; } + + public override SCG.IEnumerator<K> GetEnumerator() + { + foreach (KeyValuePair<K, V> p in sorteddict) + yield return p.Key; + } + + public override bool IsEmpty { get { return sorteddict.IsEmpty; } } + + public override int Count { [Tested]get { return sorteddict.Count; } } + + public override Speed CountSpeed { get { return sorteddict.CountSpeed; } } + + #region ISorted<K> Members + + public K FindMin() { return sorteddict.FindMin().Key; } + + public K DeleteMin() { throw new ReadOnlyCollectionException(); } + + public K FindMax() { return sorteddict.FindMax().Key; } + + public K DeleteMax() { throw new ReadOnlyCollectionException(); } + + public SCG.IComparer<K> Comparer { get { return comparer; } } + + public bool TryPredecessor(K item, out K res) + { + KeyValuePair<K, V> pRes; + bool success = sorteddict.TryPredecessor(item, out pRes); + res = pRes.Key; + return success; + } + + public bool TrySuccessor(K item, out K res) + { + KeyValuePair<K, V> pRes; + bool success = sorteddict.TrySuccessor(item, out pRes); + res = pRes.Key; + return success; + } + + public bool TryWeakPredecessor(K item, out K res) + { + KeyValuePair<K, V> pRes; + bool success = sorteddict.TryWeakPredecessor(item, out pRes); + res = pRes.Key; + return success; + } + + public bool TryWeakSuccessor(K item, out K res) + { + KeyValuePair<K, V> pRes; + bool success = sorteddict.TryWeakSuccessor(item, out pRes); + res = pRes.Key; + return success; + } + + public K Predecessor(K item) { return sorteddict.Predecessor(item).Key; } + + public K Successor(K item) { return sorteddict.Successor(item).Key; } + + public K WeakPredecessor(K item) { return sorteddict.WeakPredecessor(item).Key; } + + public K WeakSuccessor(K item) { return sorteddict.WeakSuccessor(item).Key; } + + public bool Cut(IComparable<K> c, out K low, out bool lowIsValid, out K high, out bool highIsValid) + { + KeyValuePair<K, V> lowpair, highpair; + bool retval = sorteddict.Cut(c, out lowpair, out lowIsValid, out highpair, out highIsValid); + low = lowpair.Key; + high = highpair.Key; + return retval; + } + + public IDirectedEnumerable<K> RangeFrom(K bot) + { + return new ProjectedDirectedEnumerable(sorteddict.RangeFrom(bot)); + } + + public IDirectedEnumerable<K> RangeFromTo(K bot, K top) + { + return new ProjectedDirectedEnumerable(sorteddict.RangeFromTo(bot, top)); + } + + public IDirectedEnumerable<K> RangeTo(K top) + { + return new ProjectedDirectedEnumerable(sorteddict.RangeTo(top)); + } + + public IDirectedCollectionValue<K> RangeAll() + { + return new ProjectedDirectedCollectionValue(sorteddict.RangeAll()); + } + + public void AddSorted<U>(SCG.IEnumerable<U> items) where U : K { throw new ReadOnlyCollectionException(); } + + public void RemoveRangeFrom(K low) { throw new ReadOnlyCollectionException(); } + + public void RemoveRangeFromTo(K low, K hi) { throw new ReadOnlyCollectionException(); } + + public void RemoveRangeTo(K hi) { throw new ReadOnlyCollectionException(); } + #endregion + + #region ICollection<K> Members + public Speed ContainsSpeed { get { return sorteddict.ContainsSpeed; } } + + public bool Contains(K key) { return sorteddict.Contains(key); ; } + + public int ContainsCount(K item) { return sorteddict.Contains(item) ? 1 : 0; } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public virtual ICollectionValue<K> UniqueItems() + { + return this; + } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public virtual ICollectionValue<KeyValuePair<K, int>> ItemMultiplicities() + { + return new MultiplicityOne<K>(this); + } + + + public bool ContainsAll<U>(SCG.IEnumerable<U> items) where U : K + { + //TODO: optimize? + foreach (K item in items) + if (!sorteddict.Contains(item)) + return false; + return true; + } + + public bool Find(ref K item) + { + KeyValuePair<K, V> p = new KeyValuePair<K, V>(item); + bool retval = sortedpairs.Find(ref p); + item = p.Key; + return retval; + } + + public bool FindOrAdd(ref K item) { throw new ReadOnlyCollectionException(); } + + public bool Update(K item) { throw new ReadOnlyCollectionException(); } + + public bool Update(K item, out K olditem) { throw new ReadOnlyCollectionException(); } + + public bool UpdateOrAdd(K item) { throw new ReadOnlyCollectionException(); } + + public bool UpdateOrAdd(K item, out K olditem) { throw new ReadOnlyCollectionException(); } + + public bool Remove(K item) { throw new ReadOnlyCollectionException(); } + + public bool Remove(K item, out K removeditem) { throw new ReadOnlyCollectionException(); } + + public void RemoveAllCopies(K item) { throw new ReadOnlyCollectionException(); } + + public void RemoveAll<U>(SCG.IEnumerable<U> items) where U : K { throw new ReadOnlyCollectionException(); } + + public void Clear() { throw new ReadOnlyCollectionException(); } + + public void RetainAll<U>(SCG.IEnumerable<U> items) where U : K { throw new ReadOnlyCollectionException(); } + + #endregion + + #region IExtensible<K> Members + public override bool IsReadOnly { get { return true; } } + + public bool AllowsDuplicates { get { return false; } } + + public bool DuplicatesByCounting { get { return true; } } + + public bool Add(K item) { throw new ReadOnlyCollectionException(); } + + void SCG.ICollection<K>.Add(K item) { throw new ReadOnlyCollectionException(); } + + public void AddAll(System.Collections.Generic.IEnumerable<K> items) { throw new ReadOnlyCollectionException(); } + + public void AddAll<U>(System.Collections.Generic.IEnumerable<U> items) where U : K { throw new ReadOnlyCollectionException(); } + + public bool Check() { return sorteddict.Check(); } + + #endregion + + #region IDirectedCollectionValue<K> Members + + public override IDirectedCollectionValue<K> Backwards() + { + return RangeAll().Backwards(); + } + + #endregion + + #region IDirectedEnumerable<K> Members + + IDirectedEnumerable<K> IDirectedEnumerable<K>.Backwards() { return Backwards(); } + #endregion + + #region ICloneable Members + + //TODO: test + /// <summary> + /// Make a shallow copy of this SortedKeysCollection. + /// </summary> + /// <returns></returns> + public virtual object Clone() + { + // + SortedArrayDictionary<K, V> dictclone = new SortedArrayDictionary<K, V>(sortedpairs.Count, comparer, EqualityComparer); + SortedArray<KeyValuePair<K, V>> sortedpairsclone = (SortedArray<KeyValuePair<K, V>>)(dictclone.sortedpairs); + foreach (K key in sorteddict.Keys) + { + sortedpairsclone.Add(new KeyValuePair<K, V>(key, default(V))); + } + return new SortedKeysCollection(dictclone, sortedpairsclone, comparer, EqualityComparer); + } + + #endregion + + } + + /// <summary> + /// + /// </summary> + /// <param name="stringbuilder"></param> + /// <param name="rest"></param> + /// <param name="formatProvider"></param> + /// <returns></returns> + public override bool Show(System.Text.StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider) + { + return Showing.ShowDictionary<K, V>(this, stringbuilder, ref rest, formatProvider); + } + + } + + [Serializable] + class SortedArrayDictionary<K, V> : SortedDictionaryBase<K, V>, IDictionary<K, V>, ISortedDictionary<K, V> + { + #region Constructors + + public SortedArrayDictionary() : this(Comparer<K>.Default, EqualityComparer<K>.Default) { } + + /// <summary> + /// Create a red-black tree dictionary using an external comparer for keys. + /// </summary> + /// <param name="comparer">The external comparer</param> + public SortedArrayDictionary(SCG.IComparer<K> comparer) : this(comparer, new ComparerZeroHashCodeEqualityComparer<K>(comparer)) { } + + /// <summary> + /// + /// </summary> + /// <param name="comparer"></param> + /// <param name="equalityComparer"></param> + public SortedArrayDictionary(SCG.IComparer<K> comparer, SCG.IEqualityComparer<K> equalityComparer) + : base(comparer, equalityComparer) + { + pairs = sortedpairs = new SortedArray<KeyValuePair<K, V>>(new KeyValuePairComparer<K, V>(comparer)); + } + + /// <summary> + /// + /// </summary> + /// <param name="comparer"></param> + /// <param name="equalityComparer"></param> + /// <param name="capacity"></param> + public SortedArrayDictionary(int capacity, SCG.IComparer<K> comparer, SCG.IEqualityComparer<K> equalityComparer) + : base(comparer, equalityComparer) + { + pairs = sortedpairs = new SortedArray<KeyValuePair<K, V>>(capacity, new KeyValuePairComparer<K, V>(comparer)); + } + #endregion + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public override object Clone() + { + SortedArrayDictionary<K, V> clone = new SortedArrayDictionary<K, V>(Comparer, EqualityComparer); + clone.sortedpairs.AddSorted(sortedpairs); + return clone; + } + + } }
\ No newline at end of file diff --git a/mcs/class/Mono.C5/C5/Enums.cs b/mcs/class/Mono.C5/C5/Enums.cs index a92301d83e2..bbc8da5165f 100644 --- a/mcs/class/Mono.C5/C5/Enums.cs +++ b/mcs/class/Mono.C5/C5/Enums.cs @@ -1,98 +1,98 @@ -/*
- Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-
-using System;
-using System.Diagnostics;
-using SCG = System.Collections.Generic;
-namespace C5
-{
-
- /// <summary>
- /// The symbolic characterization of the speed of lookups for a collection.
- /// The values may refer to worst-case, amortized and/or expected asymtotic
- /// complexity wrt. the collection size.
- /// </summary>
- public enum Speed : short
- {
- /// <summary>
- /// Counting the collection with the <code>Count property</code> may not return
- /// (for a synthetic and potentially infinite collection).
- /// </summary>
- PotentiallyInfinite = 1,
- /// <summary>
- /// Lookup operations like <code>Contains(T item)</code> or the <code>Count</code>
- /// property may take time O(n),
- /// where n is the size of the collection.
- /// </summary>
- Linear = 2,
- /// <summary>
- /// Lookup operations like <code>Contains(T item)</code> or the <code>Count</code>
- /// property takes time O(log n),
- /// where n is the size of the collection.
- /// </summary>
- Log = 3,
- /// <summary>
- /// Lookup operations like <code>Contains(T item)</code> or the <code>Count</code>
- /// property takes time O(1),
- /// where n is the size of the collection.
- /// </summary>
- Constant = 4
- }
- /*
- /// <summary>
- ///
- /// </summary>
- public enum ItemEqualityTypeEnum
- {
- /// <summary>
- /// Only an Equals(T,T)
- /// </summary>
- Equator,
- /// <summary>
- /// Equals(T,T) and GetHashCode(T)
- /// </summary>
- HashingEqualityComparer,
- /// <summary>
- /// Compare(T,T)
- /// </summary>
- Comparer,
- /// <summary>
- /// Compatible Compare(T,T) and GetHashCode(T)
- /// </summary>
- Both
- }
-*/
-
- /// <summary>
- /// Direction of enumeration order relative to original collection.
- /// </summary>
- public enum EnumerationDirection
- {
- /// <summary>
- /// Same direction
- /// </summary>
- Forwards,
- /// <summary>
- /// Opposite direction
- /// </summary>
- Backwards
- }
+/* + Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +using System; +using System.Diagnostics; +using SCG = System.Collections.Generic; +namespace C5 +{ + + /// <summary> + /// The symbolic characterization of the speed of lookups for a collection. + /// The values may refer to worst-case, amortized and/or expected asymtotic + /// complexity wrt. the collection size. + /// </summary> + public enum Speed : short + { + /// <summary> + /// Counting the collection with the <code>Count property</code> may not return + /// (for a synthetic and potentially infinite collection). + /// </summary> + PotentiallyInfinite = 1, + /// <summary> + /// Lookup operations like <code>Contains(T item)</code> or the <code>Count</code> + /// property may take time O(n), + /// where n is the size of the collection. + /// </summary> + Linear = 2, + /// <summary> + /// Lookup operations like <code>Contains(T item)</code> or the <code>Count</code> + /// property takes time O(log n), + /// where n is the size of the collection. + /// </summary> + Log = 3, + /// <summary> + /// Lookup operations like <code>Contains(T item)</code> or the <code>Count</code> + /// property takes time O(1), + /// where n is the size of the collection. + /// </summary> + Constant = 4 + } + /* + /// <summary> + /// + /// </summary> + public enum ItemEqualityTypeEnum + { + /// <summary> + /// Only an Equals(T,T) + /// </summary> + Equator, + /// <summary> + /// Equals(T,T) and GetHashCode(T) + /// </summary> + HashingEqualityComparer, + /// <summary> + /// Compare(T,T) + /// </summary> + Comparer, + /// <summary> + /// Compatible Compare(T,T) and GetHashCode(T) + /// </summary> + Both + } +*/ + + /// <summary> + /// Direction of enumeration order relative to original collection. + /// </summary> + public enum EnumerationDirection + { + /// <summary> + /// Same direction + /// </summary> + Forwards, + /// <summary> + /// Opposite direction + /// </summary> + Backwards + } }
\ No newline at end of file diff --git a/mcs/class/Mono.C5/C5/Events.cs b/mcs/class/Mono.C5/C5/Events.cs index 4a5d8d7d1b3..ae9575182b9 100644 --- a/mcs/class/Mono.C5/C5/Events.cs +++ b/mcs/class/Mono.C5/C5/Events.cs @@ -1,532 +1,532 @@ -/*
- Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-
-using System;
-using System.Diagnostics;
-using SCG = System.Collections.Generic;
-
-namespace C5
-{
- /// <summary>
- ///
- /// </summary>
- [Flags]
- public enum EventTypeEnum
- {
- /// <summary>
- ///
- /// </summary>
- None = 0x00000000,
- /// <summary>
- ///
- /// </summary>
- Changed = 0x00000001,
- /// <summary>
- ///
- /// </summary>
- Cleared = 0x00000002,
- /// <summary>
- ///
- /// </summary>
- Added = 0x00000004,
- /// <summary>
- ///
- /// </summary>
- Removed = 0x00000008,
- /// <summary>
- ///
- /// </summary>
- Basic = 0x0000000f,
- /// <summary>
- ///
- /// </summary>
- Inserted = 0x00000010,
- /// <summary>
- ///
- /// </summary>
- RemovedAt = 0x00000020,
- /// <summary>
- ///
- /// </summary>
- All = 0x0000003f
- }
-
- /// <summary>
- /// Holds the real events for a collection
- /// </summary>
- /// <typeparam name="T"></typeparam>
- [Serializable]
- internal sealed class EventBlock<T>
- {
- internal EventTypeEnum events;
-
- event CollectionChangedHandler<T> collectionChanged;
- internal event CollectionChangedHandler<T> CollectionChanged
- {
- add
- {
- collectionChanged += value;
- events |= EventTypeEnum.Changed;
- }
- remove
- {
- collectionChanged -= value;
- if (collectionChanged == null)
- events &= ~EventTypeEnum.Changed;
- }
- }
- internal void raiseCollectionChanged(object sender)
- { if (collectionChanged != null) collectionChanged(sender); }
-
- event CollectionClearedHandler<T> collectionCleared;
- internal event CollectionClearedHandler<T> CollectionCleared
- {
- add
- {
- collectionCleared += value;
- events |= EventTypeEnum.Cleared;
- }
- remove
- {
- collectionCleared -= value;
- if (collectionCleared == null)
- events &= ~EventTypeEnum.Cleared;
- }
- }
- internal void raiseCollectionCleared(object sender, bool full, int count)
- { if (collectionCleared != null) collectionCleared(sender, new ClearedEventArgs(full, count)); }
- internal void raiseCollectionCleared(object sender, bool full, int count, int? start)
- { if (collectionCleared != null) collectionCleared(sender, new ClearedRangeEventArgs(full, count, start)); }
-
- event ItemsAddedHandler<T> itemsAdded;
- internal event ItemsAddedHandler<T> ItemsAdded
- {
- add
- {
- itemsAdded += value;
- events |= EventTypeEnum.Added;
- }
- remove
- {
- itemsAdded -= value;
- if (itemsAdded == null)
- events &= ~EventTypeEnum.Added;
- }
- }
- internal void raiseItemsAdded(object sender, T item, int count)
- { if (itemsAdded != null) itemsAdded(sender, new ItemCountEventArgs<T>(item, count)); }
-
- event ItemsRemovedHandler<T> itemsRemoved;
- internal event ItemsRemovedHandler<T> ItemsRemoved
- {
- add
- {
- itemsRemoved += value;
- events |= EventTypeEnum.Removed;
- }
- remove
- {
- itemsRemoved -= value;
- if (itemsRemoved == null)
- events &= ~EventTypeEnum.Removed;
- }
- }
- internal void raiseItemsRemoved(object sender, T item, int count)
- { if (itemsRemoved != null) itemsRemoved(sender, new ItemCountEventArgs<T>(item, count)); }
-
- event ItemInsertedHandler<T> itemInserted;
- internal event ItemInsertedHandler<T> ItemInserted
- {
- add
- {
- itemInserted += value;
- events |= EventTypeEnum.Inserted;
- }
- remove
- {
- itemInserted -= value;
- if (itemInserted == null)
- events &= ~EventTypeEnum.Inserted;
- }
- }
- internal void raiseItemInserted(object sender, T item, int index)
- { if (itemInserted != null) itemInserted(sender, new ItemAtEventArgs<T>(item, index)); }
-
- event ItemRemovedAtHandler<T> itemRemovedAt;
- internal event ItemRemovedAtHandler<T> ItemRemovedAt
- {
- add
- {
- itemRemovedAt += value;
- events |= EventTypeEnum.RemovedAt;
- }
- remove
- {
- itemRemovedAt -= value;
- if (itemRemovedAt == null)
- events &= ~EventTypeEnum.RemovedAt;
- }
- }
- internal void raiseItemRemovedAt(object sender, T item, int index)
- { if (itemRemovedAt != null) itemRemovedAt(sender, new ItemAtEventArgs<T>(item, index)); }
- }
-
- /// <summary>
- /// Tentative, to conserve memory in GuardedCollectionValueBase
- /// This should really be nested in Guarded collection value, only have a guardereal field
- /// </summary>
- /// <typeparam name="T"></typeparam>
- [Serializable]
- internal sealed class ProxyEventBlock<T>
- {
- ICollectionValue<T> proxy, real;
-
- internal ProxyEventBlock(ICollectionValue<T> proxy, ICollectionValue<T> real)
- { this.proxy = proxy; this.real = real; }
-
- event CollectionChangedHandler<T> collectionChanged;
- CollectionChangedHandler<T> collectionChangedProxy;
- internal event CollectionChangedHandler<T> CollectionChanged
- {
- add
- {
- if (collectionChanged == null)
- {
- if (collectionChangedProxy == null)
- collectionChangedProxy = delegate(object sender) { collectionChanged(proxy); };
- real.CollectionChanged += collectionChangedProxy;
- }
- collectionChanged += value;
- }
- remove
- {
- collectionChanged -= value;
- if (collectionChanged == null)
- real.CollectionChanged -= collectionChangedProxy;
- }
- }
-
- event CollectionClearedHandler<T> collectionCleared;
- CollectionClearedHandler<T> collectionClearedProxy;
- internal event CollectionClearedHandler<T> CollectionCleared
- {
- add
- {
- if (collectionCleared == null)
- {
- if (collectionClearedProxy == null)
- collectionClearedProxy = delegate(object sender, ClearedEventArgs e) { collectionCleared(proxy, e); };
- real.CollectionCleared += collectionClearedProxy;
- }
- collectionCleared += value;
- }
- remove
- {
- collectionCleared -= value;
- if (collectionCleared == null)
- real.CollectionCleared -= collectionClearedProxy;
- }
- }
-
- event ItemsAddedHandler<T> itemsAdded;
- ItemsAddedHandler<T> itemsAddedProxy;
- internal event ItemsAddedHandler<T> ItemsAdded
- {
- add
- {
- if (itemsAdded == null)
- {
- if (itemsAddedProxy == null)
- itemsAddedProxy = delegate(object sender, ItemCountEventArgs<T> e) { itemsAdded(proxy, e); };
- real.ItemsAdded += itemsAddedProxy;
- }
- itemsAdded += value;
- }
- remove
- {
- itemsAdded -= value;
- if (itemsAdded == null)
- real.ItemsAdded -= itemsAddedProxy;
- }
- }
-
- event ItemInsertedHandler<T> itemInserted;
- ItemInsertedHandler<T> itemInsertedProxy;
- internal event ItemInsertedHandler<T> ItemInserted
- {
- add
- {
- if (itemInserted == null)
- {
- if (itemInsertedProxy == null)
- itemInsertedProxy = delegate(object sender, ItemAtEventArgs<T> e) { itemInserted(proxy, e); };
- real.ItemInserted += itemInsertedProxy;
- }
- itemInserted += value;
- }
- remove
- {
- itemInserted -= value;
- if (itemInserted == null)
- real.ItemInserted -= itemInsertedProxy;
- }
- }
-
- event ItemsRemovedHandler<T> itemsRemoved;
- ItemsRemovedHandler<T> itemsRemovedProxy;
- internal event ItemsRemovedHandler<T> ItemsRemoved
- {
- add
- {
- if (itemsRemoved == null)
- {
- if (itemsRemovedProxy == null)
- itemsRemovedProxy = delegate(object sender, ItemCountEventArgs<T> e) { itemsRemoved(proxy, e); };
- real.ItemsRemoved += itemsRemovedProxy;
- }
- itemsRemoved += value;
- }
- remove
- {
- itemsRemoved -= value;
- if (itemsRemoved == null)
- real.ItemsRemoved -= itemsRemovedProxy;
- }
- }
-
- event ItemRemovedAtHandler<T> itemRemovedAt;
- ItemRemovedAtHandler<T> itemRemovedAtProxy;
- internal event ItemRemovedAtHandler<T> ItemRemovedAt
- {
- add
- {
- if (itemRemovedAt == null)
- {
- if (itemRemovedAtProxy == null)
- itemRemovedAtProxy = delegate(object sender, ItemAtEventArgs<T> e) { itemRemovedAt(proxy, e); };
- real.ItemRemovedAt += itemRemovedAtProxy;
- }
- itemRemovedAt += value;
- }
- remove
- {
- itemRemovedAt -= value;
- if (itemRemovedAt == null)
- real.ItemRemovedAt -= itemRemovedAtProxy;
- }
- }
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <typeparam name="T"></typeparam>
- public class ItemAtEventArgs<T> : EventArgs
- {
- /// <summary>
- ///
- /// </summary>
- public readonly T Item;
- /// <summary>
- ///
- /// </summary>
- public readonly int Index;
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <param name="index"></param>
- public ItemAtEventArgs(T item, int index) { Item = item; Index = index; }
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public override string ToString()
- {
- return String.Format("(ItemAtEventArgs {0} '{1}')", Index, Item);
- }
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <typeparam name="T"></typeparam>
- public class ItemCountEventArgs<T> : EventArgs
- {
- /// <summary>
- ///
- /// </summary>
- public readonly T Item;
- /// <summary>
- ///
- /// </summary>
- public readonly int Count;
- /// <summary>
- ///
- /// </summary>
- /// <param name="count"></param>
- /// <param name="item"></param>
- public ItemCountEventArgs(T item, int count) { Item = item; Count = count; }
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public override string ToString()
- {
- return String.Format("(ItemCountEventArgs {0} '{1}')", Count, Item);
- }
- }
-
-
-
- /// <summary>
- ///
- /// </summary>
- public class ClearedEventArgs : EventArgs
- {
- /// <summary>
- ///
- /// </summary>
- public readonly bool Full;
- /// <summary>
- ///
- /// </summary>
- public readonly int Count;
- /// <summary>
- ///
- /// </summary>
- ///
- /// <param name="full">True if the operation cleared all of the collection</param>
- /// <param name="count">The number of items removed by the clear.</param>
- public ClearedEventArgs(bool full, int count) { Full = full; Count = count; }
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public override string ToString()
- {
- return String.Format("(ClearedEventArgs {0} {1})", Count, Full);
- }
- }
-
- /// <summary>
- ///
- /// </summary>
- public class ClearedRangeEventArgs : ClearedEventArgs
- {
- //WE could let this be of type int? to allow
- /// <summary>
- ///
- /// </summary>
- public readonly int? Start;
- /// <summary>
- ///
- /// </summary>
- /// <param name="full"></param>
- /// <param name="count"></param>
- /// <param name="start"></param>
- public ClearedRangeEventArgs(bool full, int count, int? start) : base(full,count) { Start = start; }
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public override string ToString()
- {
- return String.Format("(ClearedRangeEventArgs {0} {1} {2})", Count, Full, Start);
- }
- }
-
- /// <summary>
- /// The type of event raised after an operation on a collection has changed its contents.
- /// Normally, a multioperation like AddAll,
- /// <see cref="M:C5.IExtensible`1.AddAll(System.Collections.Generic.IEnumerable{`0})"/>
- /// will only fire one CollectionChanged event. Any operation that changes the collection
- /// must fire CollectionChanged as its last event.
- /// </summary>
- public delegate void CollectionChangedHandler<T>(object sender);
-
- /// <summary>
- /// The type of event raised after the Clear() operation on a collection.
- /// <para/>
- /// Note: The Clear() operation will not fire ItemsRemoved events.
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="eventArgs"></param>
- public delegate void CollectionClearedHandler<T>(object sender, ClearedEventArgs eventArgs);
-
- /// <summary>
- /// The type of event raised after an item has been added to a collection.
- /// The event will be raised at a point of time, where the collection object is
- /// in an internally consistent state and before the corresponding CollectionChanged
- /// event is raised.
- /// <para/>
- /// Note: an Update operation will fire an ItemsRemoved and an ItemsAdded event.
- /// <para/>
- /// Note: When an item is inserted into a list (<see cref="T:C5.IList`1"/>), both
- /// ItemInserted and ItemsAdded events will be fired.
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="eventArgs">An object with the item that was added</param>
- public delegate void ItemsAddedHandler<T>(object sender, ItemCountEventArgs<T> eventArgs);
-
- /// <summary>
- /// The type of event raised after an item has been removed from a collection.
- /// The event will be raised at a point of time, where the collection object is
- /// in an internally consistent state and before the corresponding CollectionChanged
- /// event is raised.
- /// <para/>
- /// Note: The Clear() operation will not fire ItemsRemoved events.
- /// <para/>
- /// Note: an Update operation will fire an ItemsRemoved and an ItemsAdded event.
- /// <para/>
- /// Note: When an item is removed from a list by the RemoveAt operation, both an
- /// ItemsRemoved and an ItemRemovedAt event will be fired.
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="eventArgs">An object with the item that was removed</param>
- public delegate void ItemsRemovedHandler<T>(object sender, ItemCountEventArgs<T> eventArgs);
-
- /// <summary>
- /// The type of event raised after an item has been inserted into a list by an Insert,
- /// InsertFirst or InsertLast operation.
- /// The event will be raised at a point of time, where the collection object is
- /// in an internally consistent state and before the corresponding CollectionChanged
- /// event is raised.
- /// <para/>
- /// Note: an ItemsAdded event will also be fired.
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="eventArgs"></param>
- public delegate void ItemInsertedHandler<T>(object sender, ItemAtEventArgs<T> eventArgs);
-
- /// <summary>
- /// The type of event raised after an item has been removed from a list by a RemoveAt(int i)
- /// operation (or RemoveFirst(), RemoveLast(), Remove() operation).
- /// The event will be raised at a point of time, where the collection object is
- /// in an internally consistent state and before the corresponding CollectionChanged
- /// event is raised.
- /// <para/>
- /// Note: an ItemRemoved event will also be fired.
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="eventArgs"></param>
- public delegate void ItemRemovedAtHandler<T>(object sender, ItemAtEventArgs<T> eventArgs);
+/* + Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +using System; +using System.Diagnostics; +using SCG = System.Collections.Generic; + +namespace C5 +{ + /// <summary> + /// + /// </summary> + [Flags] + public enum EventTypeEnum + { + /// <summary> + /// + /// </summary> + None = 0x00000000, + /// <summary> + /// + /// </summary> + Changed = 0x00000001, + /// <summary> + /// + /// </summary> + Cleared = 0x00000002, + /// <summary> + /// + /// </summary> + Added = 0x00000004, + /// <summary> + /// + /// </summary> + Removed = 0x00000008, + /// <summary> + /// + /// </summary> + Basic = 0x0000000f, + /// <summary> + /// + /// </summary> + Inserted = 0x00000010, + /// <summary> + /// + /// </summary> + RemovedAt = 0x00000020, + /// <summary> + /// + /// </summary> + All = 0x0000003f + } + + /// <summary> + /// Holds the real events for a collection + /// </summary> + /// <typeparam name="T"></typeparam> + [Serializable] + internal sealed class EventBlock<T> + { + internal EventTypeEnum events; + + event CollectionChangedHandler<T> collectionChanged; + internal event CollectionChangedHandler<T> CollectionChanged + { + add + { + collectionChanged += value; + events |= EventTypeEnum.Changed; + } + remove + { + collectionChanged -= value; + if (collectionChanged == null) + events &= ~EventTypeEnum.Changed; + } + } + internal void raiseCollectionChanged(object sender) + { if (collectionChanged != null) collectionChanged(sender); } + + event CollectionClearedHandler<T> collectionCleared; + internal event CollectionClearedHandler<T> CollectionCleared + { + add + { + collectionCleared += value; + events |= EventTypeEnum.Cleared; + } + remove + { + collectionCleared -= value; + if (collectionCleared == null) + events &= ~EventTypeEnum.Cleared; + } + } + internal void raiseCollectionCleared(object sender, bool full, int count) + { if (collectionCleared != null) collectionCleared(sender, new ClearedEventArgs(full, count)); } + internal void raiseCollectionCleared(object sender, bool full, int count, int? start) + { if (collectionCleared != null) collectionCleared(sender, new ClearedRangeEventArgs(full, count, start)); } + + event ItemsAddedHandler<T> itemsAdded; + internal event ItemsAddedHandler<T> ItemsAdded + { + add + { + itemsAdded += value; + events |= EventTypeEnum.Added; + } + remove + { + itemsAdded -= value; + if (itemsAdded == null) + events &= ~EventTypeEnum.Added; + } + } + internal void raiseItemsAdded(object sender, T item, int count) + { if (itemsAdded != null) itemsAdded(sender, new ItemCountEventArgs<T>(item, count)); } + + event ItemsRemovedHandler<T> itemsRemoved; + internal event ItemsRemovedHandler<T> ItemsRemoved + { + add + { + itemsRemoved += value; + events |= EventTypeEnum.Removed; + } + remove + { + itemsRemoved -= value; + if (itemsRemoved == null) + events &= ~EventTypeEnum.Removed; + } + } + internal void raiseItemsRemoved(object sender, T item, int count) + { if (itemsRemoved != null) itemsRemoved(sender, new ItemCountEventArgs<T>(item, count)); } + + event ItemInsertedHandler<T> itemInserted; + internal event ItemInsertedHandler<T> ItemInserted + { + add + { + itemInserted += value; + events |= EventTypeEnum.Inserted; + } + remove + { + itemInserted -= value; + if (itemInserted == null) + events &= ~EventTypeEnum.Inserted; + } + } + internal void raiseItemInserted(object sender, T item, int index) + { if (itemInserted != null) itemInserted(sender, new ItemAtEventArgs<T>(item, index)); } + + event ItemRemovedAtHandler<T> itemRemovedAt; + internal event ItemRemovedAtHandler<T> ItemRemovedAt + { + add + { + itemRemovedAt += value; + events |= EventTypeEnum.RemovedAt; + } + remove + { + itemRemovedAt -= value; + if (itemRemovedAt == null) + events &= ~EventTypeEnum.RemovedAt; + } + } + internal void raiseItemRemovedAt(object sender, T item, int index) + { if (itemRemovedAt != null) itemRemovedAt(sender, new ItemAtEventArgs<T>(item, index)); } + } + + /// <summary> + /// Tentative, to conserve memory in GuardedCollectionValueBase + /// This should really be nested in Guarded collection value, only have a guardereal field + /// </summary> + /// <typeparam name="T"></typeparam> + [Serializable] + internal sealed class ProxyEventBlock<T> + { + ICollectionValue<T> proxy, real; + + internal ProxyEventBlock(ICollectionValue<T> proxy, ICollectionValue<T> real) + { this.proxy = proxy; this.real = real; } + + event CollectionChangedHandler<T> collectionChanged; + CollectionChangedHandler<T> collectionChangedProxy; + internal event CollectionChangedHandler<T> CollectionChanged + { + add + { + if (collectionChanged == null) + { + if (collectionChangedProxy == null) + collectionChangedProxy = delegate(object sender) { collectionChanged(proxy); }; + real.CollectionChanged += collectionChangedProxy; + } + collectionChanged += value; + } + remove + { + collectionChanged -= value; + if (collectionChanged == null) + real.CollectionChanged -= collectionChangedProxy; + } + } + + event CollectionClearedHandler<T> collectionCleared; + CollectionClearedHandler<T> collectionClearedProxy; + internal event CollectionClearedHandler<T> CollectionCleared + { + add + { + if (collectionCleared == null) + { + if (collectionClearedProxy == null) + collectionClearedProxy = delegate(object sender, ClearedEventArgs e) { collectionCleared(proxy, e); }; + real.CollectionCleared += collectionClearedProxy; + } + collectionCleared += value; + } + remove + { + collectionCleared -= value; + if (collectionCleared == null) + real.CollectionCleared -= collectionClearedProxy; + } + } + + event ItemsAddedHandler<T> itemsAdded; + ItemsAddedHandler<T> itemsAddedProxy; + internal event ItemsAddedHandler<T> ItemsAdded + { + add + { + if (itemsAdded == null) + { + if (itemsAddedProxy == null) + itemsAddedProxy = delegate(object sender, ItemCountEventArgs<T> e) { itemsAdded(proxy, e); }; + real.ItemsAdded += itemsAddedProxy; + } + itemsAdded += value; + } + remove + { + itemsAdded -= value; + if (itemsAdded == null) + real.ItemsAdded -= itemsAddedProxy; + } + } + + event ItemInsertedHandler<T> itemInserted; + ItemInsertedHandler<T> itemInsertedProxy; + internal event ItemInsertedHandler<T> ItemInserted + { + add + { + if (itemInserted == null) + { + if (itemInsertedProxy == null) + itemInsertedProxy = delegate(object sender, ItemAtEventArgs<T> e) { itemInserted(proxy, e); }; + real.ItemInserted += itemInsertedProxy; + } + itemInserted += value; + } + remove + { + itemInserted -= value; + if (itemInserted == null) + real.ItemInserted -= itemInsertedProxy; + } + } + + event ItemsRemovedHandler<T> itemsRemoved; + ItemsRemovedHandler<T> itemsRemovedProxy; + internal event ItemsRemovedHandler<T> ItemsRemoved + { + add + { + if (itemsRemoved == null) + { + if (itemsRemovedProxy == null) + itemsRemovedProxy = delegate(object sender, ItemCountEventArgs<T> e) { itemsRemoved(proxy, e); }; + real.ItemsRemoved += itemsRemovedProxy; + } + itemsRemoved += value; + } + remove + { + itemsRemoved -= value; + if (itemsRemoved == null) + real.ItemsRemoved -= itemsRemovedProxy; + } + } + + event ItemRemovedAtHandler<T> itemRemovedAt; + ItemRemovedAtHandler<T> itemRemovedAtProxy; + internal event ItemRemovedAtHandler<T> ItemRemovedAt + { + add + { + if (itemRemovedAt == null) + { + if (itemRemovedAtProxy == null) + itemRemovedAtProxy = delegate(object sender, ItemAtEventArgs<T> e) { itemRemovedAt(proxy, e); }; + real.ItemRemovedAt += itemRemovedAtProxy; + } + itemRemovedAt += value; + } + remove + { + itemRemovedAt -= value; + if (itemRemovedAt == null) + real.ItemRemovedAt -= itemRemovedAtProxy; + } + } + } + + /// <summary> + /// + /// </summary> + /// <typeparam name="T"></typeparam> + public class ItemAtEventArgs<T> : EventArgs + { + /// <summary> + /// + /// </summary> + public readonly T Item; + /// <summary> + /// + /// </summary> + public readonly int Index; + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <param name="index"></param> + public ItemAtEventArgs(T item, int index) { Item = item; Index = index; } + /// <summary> + /// + /// </summary> + /// <returns></returns> + public override string ToString() + { + return String.Format("(ItemAtEventArgs {0} '{1}')", Index, Item); + } + } + + /// <summary> + /// + /// </summary> + /// <typeparam name="T"></typeparam> + public class ItemCountEventArgs<T> : EventArgs + { + /// <summary> + /// + /// </summary> + public readonly T Item; + /// <summary> + /// + /// </summary> + public readonly int Count; + /// <summary> + /// + /// </summary> + /// <param name="count"></param> + /// <param name="item"></param> + public ItemCountEventArgs(T item, int count) { Item = item; Count = count; } + /// <summary> + /// + /// </summary> + /// <returns></returns> + public override string ToString() + { + return String.Format("(ItemCountEventArgs {0} '{1}')", Count, Item); + } + } + + + + /// <summary> + /// + /// </summary> + public class ClearedEventArgs : EventArgs + { + /// <summary> + /// + /// </summary> + public readonly bool Full; + /// <summary> + /// + /// </summary> + public readonly int Count; + /// <summary> + /// + /// </summary> + /// + /// <param name="full">True if the operation cleared all of the collection</param> + /// <param name="count">The number of items removed by the clear.</param> + public ClearedEventArgs(bool full, int count) { Full = full; Count = count; } + /// <summary> + /// + /// </summary> + /// <returns></returns> + public override string ToString() + { + return String.Format("(ClearedEventArgs {0} {1})", Count, Full); + } + } + + /// <summary> + /// + /// </summary> + public class ClearedRangeEventArgs : ClearedEventArgs + { + //WE could let this be of type int? to allow + /// <summary> + /// + /// </summary> + public readonly int? Start; + /// <summary> + /// + /// </summary> + /// <param name="full"></param> + /// <param name="count"></param> + /// <param name="start"></param> + public ClearedRangeEventArgs(bool full, int count, int? start) : base(full,count) { Start = start; } + /// <summary> + /// + /// </summary> + /// <returns></returns> + public override string ToString() + { + return String.Format("(ClearedRangeEventArgs {0} {1} {2})", Count, Full, Start); + } + } + + /// <summary> + /// The type of event raised after an operation on a collection has changed its contents. + /// Normally, a multioperation like AddAll, + /// <see cref="M:C5.IExtensible`1.AddAll(System.Collections.Generic.IEnumerable{`0})"/> + /// will only fire one CollectionChanged event. Any operation that changes the collection + /// must fire CollectionChanged as its last event. + /// </summary> + public delegate void CollectionChangedHandler<T>(object sender); + + /// <summary> + /// The type of event raised after the Clear() operation on a collection. + /// <para/> + /// Note: The Clear() operation will not fire ItemsRemoved events. + /// </summary> + /// <param name="sender"></param> + /// <param name="eventArgs"></param> + public delegate void CollectionClearedHandler<T>(object sender, ClearedEventArgs eventArgs); + + /// <summary> + /// The type of event raised after an item has been added to a collection. + /// The event will be raised at a point of time, where the collection object is + /// in an internally consistent state and before the corresponding CollectionChanged + /// event is raised. + /// <para/> + /// Note: an Update operation will fire an ItemsRemoved and an ItemsAdded event. + /// <para/> + /// Note: When an item is inserted into a list (<see cref="T:C5.IList`1"/>), both + /// ItemInserted and ItemsAdded events will be fired. + /// </summary> + /// <param name="sender"></param> + /// <param name="eventArgs">An object with the item that was added</param> + public delegate void ItemsAddedHandler<T>(object sender, ItemCountEventArgs<T> eventArgs); + + /// <summary> + /// The type of event raised after an item has been removed from a collection. + /// The event will be raised at a point of time, where the collection object is + /// in an internally consistent state and before the corresponding CollectionChanged + /// event is raised. + /// <para/> + /// Note: The Clear() operation will not fire ItemsRemoved events. + /// <para/> + /// Note: an Update operation will fire an ItemsRemoved and an ItemsAdded event. + /// <para/> + /// Note: When an item is removed from a list by the RemoveAt operation, both an + /// ItemsRemoved and an ItemRemovedAt event will be fired. + /// </summary> + /// <param name="sender"></param> + /// <param name="eventArgs">An object with the item that was removed</param> + public delegate void ItemsRemovedHandler<T>(object sender, ItemCountEventArgs<T> eventArgs); + + /// <summary> + /// The type of event raised after an item has been inserted into a list by an Insert, + /// InsertFirst or InsertLast operation. + /// The event will be raised at a point of time, where the collection object is + /// in an internally consistent state and before the corresponding CollectionChanged + /// event is raised. + /// <para/> + /// Note: an ItemsAdded event will also be fired. + /// </summary> + /// <param name="sender"></param> + /// <param name="eventArgs"></param> + public delegate void ItemInsertedHandler<T>(object sender, ItemAtEventArgs<T> eventArgs); + + /// <summary> + /// The type of event raised after an item has been removed from a list by a RemoveAt(int i) + /// operation (or RemoveFirst(), RemoveLast(), Remove() operation). + /// The event will be raised at a point of time, where the collection object is + /// in an internally consistent state and before the corresponding CollectionChanged + /// event is raised. + /// <para/> + /// Note: an ItemRemoved event will also be fired. + /// </summary> + /// <param name="sender"></param> + /// <param name="eventArgs"></param> + public delegate void ItemRemovedAtHandler<T>(object sender, ItemAtEventArgs<T> eventArgs); }
\ No newline at end of file diff --git a/mcs/class/Mono.C5/C5/Exceptions.cs b/mcs/class/Mono.C5/C5/Exceptions.cs index 2480b22c6df..7be7625f8b2 100644 --- a/mcs/class/Mono.C5/C5/Exceptions.cs +++ b/mcs/class/Mono.C5/C5/Exceptions.cs @@ -1,244 +1,244 @@ -/*
- Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-
-using System;
-using System.Diagnostics;
-using SCG = System.Collections.Generic;
-
-namespace C5
-{
- /// <summary>
- /// An exception to throw from library code when an internal inconsistency is encountered.
- /// </summary>
- public class InternalException : Exception
- {
- internal InternalException(string message) : base(message) { }
- }
-
- /// <summary>
- /// An exception thrown by an update operation on a Read-Only collection or dictionary.
- /// <para>This exception will be thrown unconditionally when an update operation
- /// (method or set property) is called. No check is made to see if the update operation,
- /// if allowed, would actually change the collection. </para>
- /// </summary>
- [Serializable]
- public class ReadOnlyCollectionException : Exception
- {
- /// <summary>
- /// Create a simple exception with no further explanation.
- /// </summary>
- public ReadOnlyCollectionException() : base() { }
- /// <summary>
- /// Create the exception with an explanation of the reason.
- /// </summary>
- /// <param name="message"></param>
- public ReadOnlyCollectionException(string message) : base(message) { }
- }
-
- /// <summary>
- ///
- /// </summary>
- [Serializable]
- public class FixedSizeCollectionException : Exception
- {
- /// <summary>
- /// Create a simple exception with no further explanation.
- /// </summary>
- public FixedSizeCollectionException() : base() { }
- /// <summary>
- /// Create the exception with an explanation of the reason.
- /// </summary>
- /// <param name="message"></param>
- public FixedSizeCollectionException(string message) : base(message) { }
- }
-
- /// <summary>
- ///
- /// </summary>
- [Serializable]
- public class UnlistenableEventException : Exception
- {
- /// <summary>
- /// Create a simple exception with no further explanation.
- /// </summary>
- public UnlistenableEventException() : base() { }
- /// <summary>
- /// Create the exception with an explanation of the reason.
- /// </summary>
- /// <param name="message"></param>
- public UnlistenableEventException(string message) : base(message) { }
- }
-
- /// <summary>
- /// An exception thrown by enumerators, range views etc. when accessed after
- /// the underlying collection has been modified.
- /// </summary>
- [Serializable]
- public class CollectionModifiedException : Exception
- {
- /// <summary>
- /// Create a simple exception with no further explanation.
- /// </summary>
- public CollectionModifiedException() : base() { }
- /// <summary>
- /// Create the exception with an explanation of the reason.
- /// </summary>
- /// <param name="message"></param>
- public CollectionModifiedException(string message) : base(message) { }
- }
-
- /// <summary>
- /// An excption thrown when trying to access a view (a list view on a <see cref="T:C5.IList`1"/> or
- /// a snapshot on a <see cref="T:C5.IPersistentSorted`1"/>)
- /// that has been invalidated by some earlier operation.
- /// <para>
- /// The typical scenario is a view on a list that hash been invalidated by a call to
- /// Sort, Reverse or Shuffle on some other, overlapping view or the whole list.
- /// </para>
- /// </summary>
- [Serializable]
- public class ViewDisposedException : Exception
- {
- /// <summary>
- /// Create a simple exception with no further explanation.
- /// </summary>
- public ViewDisposedException() : base() { }
- /// <summary>
- /// Create the exception with an explanation of the reason.
- /// </summary>
- /// <param name="message"></param>
- public ViewDisposedException(string message) : base(message) { }
- }
-
- /// <summary>
- /// An exception thrown by a lookup or lookup with update operation that does not
- /// find the lookup item and has no other means to communicate failure.
- /// <para>The typical scenario is a lookup by key in a dictionary with an indexer,
- /// see e.g. <see cref="P:C5.IDictionary`2.Item(`0)"/></para>
- /// </summary>
- [Serializable]
- public class NoSuchItemException : Exception
- {
- /// <summary>
- /// Create a simple exception with no further explanation.
- /// </summary>
- public NoSuchItemException() : base() { }
- /// <summary>
- /// Create the exception with an explanation of the reason.
- /// </summary>
- /// <param name="message"></param>
- public NoSuchItemException(string message) : base(message) { }
- }
-
- /// <summary>
- /// An exception thrown by an operation on a list (<see cref="T:C5.IList`1"/>)
- /// that only makes sense for a view, not for an underlying list.
- /// </summary>
- [Serializable]
- public class NotAViewException : Exception
- {
- /// <summary>
- /// Create a simple exception with no further explanation.
- /// </summary>
- public NotAViewException() : base() { }
- /// <summary>
- /// Create the exception with an explanation of the reason.
- /// </summary>
- /// <param name="message"></param>
- public NotAViewException(string message) : base(message) { }
- }
-
- /// <summary>
- /// An exception thrown when an operation attempts to create a duplicate in a collection with set semantics
- /// (<see cref="P:C5.IExtensible`1.AllowsDuplicates"/> is false) or attempts to create a duplicate key in a dictionary.
- /// <para>With collections this can only happen with Insert operations on lists, since the Add operations will
- /// not try to create duplictes and either ignore the failure or report it in a bool return value.
- /// </para>
- /// <para>With dictionaries this can happen with the <see cref="M:C5.IDictionary`2.Add(`0,`1)"/> metod.</para>
- /// </summary>
- [Serializable]
- public class DuplicateNotAllowedException : Exception
- {
- /// <summary>
- /// Create a simple exception with no further explanation.
- /// </summary>
- public DuplicateNotAllowedException() : base() { }
- /// <summary>
- /// Create the exception with an explanation of the reason.
- /// </summary>
- /// <param name="message"></param>
- public DuplicateNotAllowedException(string message) : base(message) { }
- }
-
- /// <summary>
- ///
- /// </summary>
- [Serializable]
- public class InvalidPriorityQueueHandleException : Exception
- {
- /// <summary>
- /// Create a simple exception with no further explanation.
- /// </summary>
- public InvalidPriorityQueueHandleException() : base() { }
- /// <summary>
- /// Create the exception with an explanation of the reason.
- /// </summary>
- /// <param name="message"></param>
- public InvalidPriorityQueueHandleException(string message) : base(message) { }
- }
-
- /// <summary>
- /// An exception thrown by an operation that need to construct a natural
- /// comparer for a type.
- /// </summary>
- [Serializable]
- public class NotComparableException : Exception
- {
- /// <summary>
- /// Create a simple exception with no further explanation.
- /// </summary>
- public NotComparableException() : base() { }
- /// <summary>
- /// Create the exception with an explanation of the reason.
- /// </summary>
- /// <param name="message"></param>
- public NotComparableException(string message) : base(message) { }
- }
-
- /// <summary>
- /// An exception thrown by operations on a list that expects an argument
- /// that is a view on the same underlying list.
- /// </summary>
- [Serializable]
- public class IncompatibleViewException : Exception
- {
- /// <summary>
- /// Create a simple exception with no further explanation.
- /// </summary>
- public IncompatibleViewException() : base() { }
- /// <summary>
- /// Create the exception with an explanation of the reason.
- /// </summary>
- /// <param name="message"></param>
- public IncompatibleViewException(string message) : base(message) { }
- }
-
+/* + Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +using System; +using System.Diagnostics; +using SCG = System.Collections.Generic; + +namespace C5 +{ + /// <summary> + /// An exception to throw from library code when an internal inconsistency is encountered. + /// </summary> + public class InternalException : Exception + { + internal InternalException(string message) : base(message) { } + } + + /// <summary> + /// An exception thrown by an update operation on a Read-Only collection or dictionary. + /// <para>This exception will be thrown unconditionally when an update operation + /// (method or set property) is called. No check is made to see if the update operation, + /// if allowed, would actually change the collection. </para> + /// </summary> + [Serializable] + public class ReadOnlyCollectionException : Exception + { + /// <summary> + /// Create a simple exception with no further explanation. + /// </summary> + public ReadOnlyCollectionException() : base() { } + /// <summary> + /// Create the exception with an explanation of the reason. + /// </summary> + /// <param name="message"></param> + public ReadOnlyCollectionException(string message) : base(message) { } + } + + /// <summary> + /// + /// </summary> + [Serializable] + public class FixedSizeCollectionException : Exception + { + /// <summary> + /// Create a simple exception with no further explanation. + /// </summary> + public FixedSizeCollectionException() : base() { } + /// <summary> + /// Create the exception with an explanation of the reason. + /// </summary> + /// <param name="message"></param> + public FixedSizeCollectionException(string message) : base(message) { } + } + + /// <summary> + /// + /// </summary> + [Serializable] + public class UnlistenableEventException : Exception + { + /// <summary> + /// Create a simple exception with no further explanation. + /// </summary> + public UnlistenableEventException() : base() { } + /// <summary> + /// Create the exception with an explanation of the reason. + /// </summary> + /// <param name="message"></param> + public UnlistenableEventException(string message) : base(message) { } + } + + /// <summary> + /// An exception thrown by enumerators, range views etc. when accessed after + /// the underlying collection has been modified. + /// </summary> + [Serializable] + public class CollectionModifiedException : Exception + { + /// <summary> + /// Create a simple exception with no further explanation. + /// </summary> + public CollectionModifiedException() : base() { } + /// <summary> + /// Create the exception with an explanation of the reason. + /// </summary> + /// <param name="message"></param> + public CollectionModifiedException(string message) : base(message) { } + } + + /// <summary> + /// An excption thrown when trying to access a view (a list view on a <see cref="T:C5.IList`1"/> or + /// a snapshot on a <see cref="T:C5.IPersistentSorted`1"/>) + /// that has been invalidated by some earlier operation. + /// <para> + /// The typical scenario is a view on a list that hash been invalidated by a call to + /// Sort, Reverse or Shuffle on some other, overlapping view or the whole list. + /// </para> + /// </summary> + [Serializable] + public class ViewDisposedException : Exception + { + /// <summary> + /// Create a simple exception with no further explanation. + /// </summary> + public ViewDisposedException() : base() { } + /// <summary> + /// Create the exception with an explanation of the reason. + /// </summary> + /// <param name="message"></param> + public ViewDisposedException(string message) : base(message) { } + } + + /// <summary> + /// An exception thrown by a lookup or lookup with update operation that does not + /// find the lookup item and has no other means to communicate failure. + /// <para>The typical scenario is a lookup by key in a dictionary with an indexer, + /// see e.g. <see cref="P:C5.IDictionary`2.Item(`0)"/></para> + /// </summary> + [Serializable] + public class NoSuchItemException : Exception + { + /// <summary> + /// Create a simple exception with no further explanation. + /// </summary> + public NoSuchItemException() : base() { } + /// <summary> + /// Create the exception with an explanation of the reason. + /// </summary> + /// <param name="message"></param> + public NoSuchItemException(string message) : base(message) { } + } + + /// <summary> + /// An exception thrown by an operation on a list (<see cref="T:C5.IList`1"/>) + /// that only makes sense for a view, not for an underlying list. + /// </summary> + [Serializable] + public class NotAViewException : Exception + { + /// <summary> + /// Create a simple exception with no further explanation. + /// </summary> + public NotAViewException() : base() { } + /// <summary> + /// Create the exception with an explanation of the reason. + /// </summary> + /// <param name="message"></param> + public NotAViewException(string message) : base(message) { } + } + + /// <summary> + /// An exception thrown when an operation attempts to create a duplicate in a collection with set semantics + /// (<see cref="P:C5.IExtensible`1.AllowsDuplicates"/> is false) or attempts to create a duplicate key in a dictionary. + /// <para>With collections this can only happen with Insert operations on lists, since the Add operations will + /// not try to create duplictes and either ignore the failure or report it in a bool return value. + /// </para> + /// <para>With dictionaries this can happen with the <see cref="M:C5.IDictionary`2.Add(`0,`1)"/> metod.</para> + /// </summary> + [Serializable] + public class DuplicateNotAllowedException : Exception + { + /// <summary> + /// Create a simple exception with no further explanation. + /// </summary> + public DuplicateNotAllowedException() : base() { } + /// <summary> + /// Create the exception with an explanation of the reason. + /// </summary> + /// <param name="message"></param> + public DuplicateNotAllowedException(string message) : base(message) { } + } + + /// <summary> + /// + /// </summary> + [Serializable] + public class InvalidPriorityQueueHandleException : Exception + { + /// <summary> + /// Create a simple exception with no further explanation. + /// </summary> + public InvalidPriorityQueueHandleException() : base() { } + /// <summary> + /// Create the exception with an explanation of the reason. + /// </summary> + /// <param name="message"></param> + public InvalidPriorityQueueHandleException(string message) : base(message) { } + } + + /// <summary> + /// An exception thrown by an operation that need to construct a natural + /// comparer for a type. + /// </summary> + [Serializable] + public class NotComparableException : Exception + { + /// <summary> + /// Create a simple exception with no further explanation. + /// </summary> + public NotComparableException() : base() { } + /// <summary> + /// Create the exception with an explanation of the reason. + /// </summary> + /// <param name="message"></param> + public NotComparableException(string message) : base(message) { } + } + + /// <summary> + /// An exception thrown by operations on a list that expects an argument + /// that is a view on the same underlying list. + /// </summary> + [Serializable] + public class IncompatibleViewException : Exception + { + /// <summary> + /// Create a simple exception with no further explanation. + /// </summary> + public IncompatibleViewException() : base() { } + /// <summary> + /// Create the exception with an explanation of the reason. + /// </summary> + /// <param name="message"></param> + public IncompatibleViewException(string message) : base(message) { } + } + }
\ No newline at end of file diff --git a/mcs/class/Mono.C5/C5/Formatting.cs b/mcs/class/Mono.C5/C5/Formatting.cs index 8dffa7fcd22..7164b693b0c 100644 --- a/mcs/class/Mono.C5/C5/Formatting.cs +++ b/mcs/class/Mono.C5/C5/Formatting.cs @@ -1,249 +1,249 @@ -/*
- Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-
-using C5;
-using System;
-using System.Reflection;
-using System.Reflection.Emit;
-using System.Diagnostics;
-using System.Text;
-
-namespace C5
-{
- /// <summary>
- /// <i>(Describe usage of "L:300" format string.)</i>
- /// </summary>
- public interface IShowable : IFormattable
- {
- //TODO: wonder if we should use TextWriters instead of StringBuilders?
- /// <summary>
- /// Format <code>this</code> using at most approximately <code>rest</code> chars and
- /// append the result, possibly truncated, to stringbuilder.
- /// Subtract the actual number of used chars from <code>rest</code>.
- /// </summary>
- /// <param name="stringbuilder"></param>
- /// <param name="rest"></param>
- /// <param name="formatProvider"></param>
- /// <returns>True if the appended formatted string was complete (not truncated).</returns>
- bool Show(StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider);
- }
- // ------------------------------------------------------------
-
- // Static helper methods for Showing collections
-
- /// <summary>
- ///
- /// </summary>
- public static class Showing
- {
- /// <summary>
- /// Show <code>Object obj</code> by appending it to <code>stringbuilder</code>
- /// </summary>
- /// <param name="obj"></param>
- /// <param name="stringbuilder"></param>
- /// <param name="rest"></param>
- /// <param name="formatProvider"></param>
- /// <returns>True if <code>obj</code> was shown completely.</returns>
- public static bool Show(Object obj, StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider)
- {
- IShowable showable;
- if (rest <= 0)
- return false;
- else if ((showable = obj as IShowable) != null)
- return showable.Show(stringbuilder, ref rest, formatProvider);
- int oldLength = stringbuilder.Length;
- stringbuilder.AppendFormat(formatProvider, "{0}", obj);
- rest -= (stringbuilder.Length - oldLength);
- return true;
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="showable"></param>
- /// <param name="format"></param>
- /// <param name="formatProvider"></param>
- /// <returns></returns>
- public static String ShowString(IShowable showable, String format, IFormatProvider formatProvider)
- {
- int rest = maxLength(format);
- StringBuilder sb = new StringBuilder();
- showable.Show(sb, ref rest, formatProvider);
- return sb.ToString();
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="format"></param>
- /// <returns></returns>
- static int maxLength(String format)
- {
- //TODO: validate format string
- if (format == null)
- return 80;
- if (format.Length > 1 && format.StartsWith("L"))
- {
- return int.Parse(format.Substring(1));
- }
- else
- return int.MaxValue;
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="items"></param>
- /// <param name="stringbuilder"></param>
- /// <param name="rest"></param>
- /// <param name="formatProvider"></param>
- /// <returns>True if collection was shown completely</returns>
- public static bool ShowCollectionValue<T>(ICollectionValue<T> items, StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider)
- {
- string startdelim = "{ ", enddelim = " }";
- bool showIndexes = false;
- bool showMultiplicities = false;
- //TODO: do not test here at run time, but select code at compile time
- // perhaps by delivering the print type to this metod
- IList<T> list;
- ICollection<T> coll = items as ICollection<T>;
- if ((list = items as IList<T>) != null)
- {
- startdelim = "[ ";
- enddelim = " ]";
- //TODO: should have been (items as IIndexed<T>).IndexingSpeed
- showIndexes = list.IndexingSpeed == Speed.Constant;
- }
- else if (coll != null)
- {
- if (coll.AllowsDuplicates)
- {
- startdelim = "{{ ";
- enddelim = " }}";
- if (coll.DuplicatesByCounting)
- showMultiplicities = true;
- }
- }
-
- stringbuilder.Append(startdelim);
- rest -= 2 * startdelim.Length;
- bool first = true;
- bool complete = true;
- int index = 0;
-
- if (showMultiplicities)
- {
- foreach (KeyValuePair<T, int> p in coll.ItemMultiplicities())
- {
- complete = false;
- if (rest <= 0)
- break;
- if (first)
- first = false;
- else
- {
- stringbuilder.Append(", ");
- rest -= 2;
- }
- if (complete = Showing.Show(p.Key, stringbuilder, ref rest, formatProvider))
- {
- string multiplicityString = string.Format("(*{0})", p.Value);
- stringbuilder.Append(multiplicityString);
- rest -= multiplicityString.Length;
- }
- }
- }
- else
- {
- foreach (T x in items)
- {
- complete = false;
- if (rest <= 0)
- break;
- if (first)
- first = false;
- else
- {
- stringbuilder.Append(", ");
- rest -= 2;
- }
- if (showIndexes)
- {
- string indexString = string.Format("{0}:", index++);
- stringbuilder.Append(indexString);
- rest -= indexString.Length;
- }
- complete = Showing.Show(x, stringbuilder, ref rest, formatProvider);
- }
- }
- if (!complete)
- {
- stringbuilder.Append("...");
- rest -= 3;
- }
- stringbuilder.Append(enddelim);
- return complete;
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <typeparam name="K"></typeparam>
- /// <typeparam name="V"></typeparam>
- ///
- /// <param name="dictionary"></param>
- /// <param name="stringbuilder"></param>
- /// <param name="formatProvider"></param>
- /// <param name="rest"></param>
- /// <returns></returns>
- public static bool ShowDictionary<K, V>(IDictionary<K, V> dictionary, StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider)
- {
- bool sorted = dictionary is ISortedDictionary<K, V>;
- stringbuilder.Append(sorted ? "[ " : "{ ");
- rest -= 4; // Account for "( " and " )"
- bool first = true;
- bool complete = true;
-
- foreach (KeyValuePair<K, V> p in dictionary)
- {
- complete = false;
- if (rest <= 0)
- break;
- if (first)
- first = false;
- else
- {
- stringbuilder.Append(", ");
- rest -= 2;
- }
- complete = Showing.Show(p, stringbuilder, ref rest, formatProvider);
- }
- if (!complete)
- {
- stringbuilder.Append("...");
- rest -= 3;
- }
- stringbuilder.Append(sorted ? " ]" : " }");
- return complete;
- }
- }
+/* + Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +using C5; +using System; +using System.Reflection; +using System.Reflection.Emit; +using System.Diagnostics; +using System.Text; + +namespace C5 +{ + /// <summary> + /// <i>(Describe usage of "L:300" format string.)</i> + /// </summary> + public interface IShowable : IFormattable + { + //TODO: wonder if we should use TextWriters instead of StringBuilders? + /// <summary> + /// Format <code>this</code> using at most approximately <code>rest</code> chars and + /// append the result, possibly truncated, to stringbuilder. + /// Subtract the actual number of used chars from <code>rest</code>. + /// </summary> + /// <param name="stringbuilder"></param> + /// <param name="rest"></param> + /// <param name="formatProvider"></param> + /// <returns>True if the appended formatted string was complete (not truncated).</returns> + bool Show(StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider); + } + // ------------------------------------------------------------ + + // Static helper methods for Showing collections + + /// <summary> + /// + /// </summary> + public static class Showing + { + /// <summary> + /// Show <code>Object obj</code> by appending it to <code>stringbuilder</code> + /// </summary> + /// <param name="obj"></param> + /// <param name="stringbuilder"></param> + /// <param name="rest"></param> + /// <param name="formatProvider"></param> + /// <returns>True if <code>obj</code> was shown completely.</returns> + public static bool Show(Object obj, StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider) + { + IShowable showable; + if (rest <= 0) + return false; + else if ((showable = obj as IShowable) != null) + return showable.Show(stringbuilder, ref rest, formatProvider); + int oldLength = stringbuilder.Length; + stringbuilder.AppendFormat(formatProvider, "{0}", obj); + rest -= (stringbuilder.Length - oldLength); + return true; + } + + /// <summary> + /// + /// </summary> + /// <param name="showable"></param> + /// <param name="format"></param> + /// <param name="formatProvider"></param> + /// <returns></returns> + public static String ShowString(IShowable showable, String format, IFormatProvider formatProvider) + { + int rest = maxLength(format); + StringBuilder sb = new StringBuilder(); + showable.Show(sb, ref rest, formatProvider); + return sb.ToString(); + } + + /// <summary> + /// + /// </summary> + /// <param name="format"></param> + /// <returns></returns> + static int maxLength(String format) + { + //TODO: validate format string + if (format == null) + return 80; + if (format.Length > 1 && format.StartsWith("L")) + { + return int.Parse(format.Substring(1)); + } + else + return int.MaxValue; + } + + /// <summary> + /// + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="items"></param> + /// <param name="stringbuilder"></param> + /// <param name="rest"></param> + /// <param name="formatProvider"></param> + /// <returns>True if collection was shown completely</returns> + public static bool ShowCollectionValue<T>(ICollectionValue<T> items, StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider) + { + string startdelim = "{ ", enddelim = " }"; + bool showIndexes = false; + bool showMultiplicities = false; + //TODO: do not test here at run time, but select code at compile time + // perhaps by delivering the print type to this metod + IList<T> list; + ICollection<T> coll = items as ICollection<T>; + if ((list = items as IList<T>) != null) + { + startdelim = "[ "; + enddelim = " ]"; + //TODO: should have been (items as IIndexed<T>).IndexingSpeed + showIndexes = list.IndexingSpeed == Speed.Constant; + } + else if (coll != null) + { + if (coll.AllowsDuplicates) + { + startdelim = "{{ "; + enddelim = " }}"; + if (coll.DuplicatesByCounting) + showMultiplicities = true; + } + } + + stringbuilder.Append(startdelim); + rest -= 2 * startdelim.Length; + bool first = true; + bool complete = true; + int index = 0; + + if (showMultiplicities) + { + foreach (KeyValuePair<T, int> p in coll.ItemMultiplicities()) + { + complete = false; + if (rest <= 0) + break; + if (first) + first = false; + else + { + stringbuilder.Append(", "); + rest -= 2; + } + if (complete = Showing.Show(p.Key, stringbuilder, ref rest, formatProvider)) + { + string multiplicityString = string.Format("(*{0})", p.Value); + stringbuilder.Append(multiplicityString); + rest -= multiplicityString.Length; + } + } + } + else + { + foreach (T x in items) + { + complete = false; + if (rest <= 0) + break; + if (first) + first = false; + else + { + stringbuilder.Append(", "); + rest -= 2; + } + if (showIndexes) + { + string indexString = string.Format("{0}:", index++); + stringbuilder.Append(indexString); + rest -= indexString.Length; + } + complete = Showing.Show(x, stringbuilder, ref rest, formatProvider); + } + } + if (!complete) + { + stringbuilder.Append("..."); + rest -= 3; + } + stringbuilder.Append(enddelim); + return complete; + } + + /// <summary> + /// + /// </summary> + /// <typeparam name="K"></typeparam> + /// <typeparam name="V"></typeparam> + /// + /// <param name="dictionary"></param> + /// <param name="stringbuilder"></param> + /// <param name="formatProvider"></param> + /// <param name="rest"></param> + /// <returns></returns> + public static bool ShowDictionary<K, V>(IDictionary<K, V> dictionary, StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider) + { + bool sorted = dictionary is ISortedDictionary<K, V>; + stringbuilder.Append(sorted ? "[ " : "{ "); + rest -= 4; // Account for "( " and " )" + bool first = true; + bool complete = true; + + foreach (KeyValuePair<K, V> p in dictionary) + { + complete = false; + if (rest <= 0) + break; + if (first) + first = false; + else + { + stringbuilder.Append(", "); + rest -= 2; + } + complete = Showing.Show(p, stringbuilder, ref rest, formatProvider); + } + if (!complete) + { + stringbuilder.Append("..."); + rest -= 3; + } + stringbuilder.Append(sorted ? " ]" : " }"); + return complete; + } + } }
\ No newline at end of file diff --git a/mcs/class/Mono.C5/C5/Hashers.cs b/mcs/class/Mono.C5/C5/Hashers.cs index 6c1eaadedd7..2389239b76c 100644 --- a/mcs/class/Mono.C5/C5/Hashers.cs +++ b/mcs/class/Mono.C5/C5/Hashers.cs @@ -1,523 +1,523 @@ -/*
- Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-
-using C5;
-using System;
-using System.Reflection;
-using System.Reflection.Emit;
-using System.Diagnostics;
-using SCG = System.Collections.Generic;
-
-namespace C5
-{
- /// <summary>
- /// Utility class for building default generic equalityComparers.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- public static class EqualityComparer<T>
- {
- readonly static Type isequenced = typeof(ISequenced<>);
-
- readonly static Type icollection = typeof(ICollection<>);
-
- readonly static Type orderedcollectionequalityComparer = typeof(SequencedCollectionEqualityComparer<,>);
-
- readonly static Type unorderedcollectionequalityComparer = typeof(UnsequencedCollectionEqualityComparer<,>);
-
- readonly static Type equalityequalityComparer = typeof(EquatableEqualityComparer<>);
-
- readonly static Type iequalitytype = typeof(IEquatable<T>);
-
- static SCG.IEqualityComparer<T> cachedDefault = null;
-
- //TODO: find the right word for initialized+invocation
- /// <summary>
- /// A default generic equality comparer for type T. The procedure is as follows:
- /// <list>
- /// <item>If T is a primitive type (char, sbyte, byte, short, ushort, int, uint, float, double, decimal),
- /// the equalityComparer will be a standard equalityComparer for that type</item>
- /// <item>If the actual generic argument T implements the generic interface
- /// <see cref="T:C5.ISequenced`1"/> for some value W of its generic parameter T,
- /// the equalityComparer will be <see cref="T:C5.SequencedCollectionEqualityComparer`2"/></item>
- /// <item>If the actual generic argument T implements
- /// <see cref="T:C5.ICollection`1"/> for some value W of its generic parameter T,
- /// the equalityComparer will be <see cref="T:C5.UnsequencedCollectionEqualityComparer`2"/></item>
- /// <item>If T is a type implementing <see cref="T:C5.IEquatable`1"/>, the equalityComparer
- /// will be <see cref="T:C5.EquatableEqualityComparer`1"/></item>
- /// <item>If T is a type not implementing <see cref="T:C5.IEquatable`1"/>, the equalityComparer
- /// will be <see cref="T:C5.NaturalEqualityComparer`1"/> </item>
- /// </list>
- /// The <see cref="T:C5.IEqualityComparer`1"/> object is constructed when this class is initialised, i.e.
- /// its static constructors called. Thus, the property will be the same object
- /// for the duration of an invocation of the runtime, but a value serialized in
- /// another invocation and deserialized here will not be the same object.
- /// </summary>
- /// <value></value>
- public static SCG.IEqualityComparer<T> Default
- {
- get
- {
- if (cachedDefault != null)
- return cachedDefault;
-
- Type t = typeof(T);
-
- if (t.IsValueType)
- {
- if (t.Equals(typeof(char)))
- return cachedDefault = (SCG.IEqualityComparer<T>)(CharEqualityComparer.Default);
-
- if (t.Equals(typeof(sbyte)))
- return cachedDefault = (SCG.IEqualityComparer<T>)(SByteEqualityComparer.Default);
-
- if (t.Equals(typeof(byte)))
- return cachedDefault = (SCG.IEqualityComparer<T>)(ByteEqualityComparer.Default);
-
- if (t.Equals(typeof(short)))
- return cachedDefault = (SCG.IEqualityComparer<T>)(ShortEqualityComparer.Default);
-
- if (t.Equals(typeof(ushort)))
- return cachedDefault = (SCG.IEqualityComparer<T>)(UShortEqualityComparer.Default);
-
- if (t.Equals(typeof(int)))
- return cachedDefault = (SCG.IEqualityComparer<T>)(IntEqualityComparer.Default);
-
- if (t.Equals(typeof(uint)))
- return cachedDefault = (SCG.IEqualityComparer<T>)(UIntEqualityComparer.Default);
-
- if (t.Equals(typeof(long)))
- return cachedDefault = (SCG.IEqualityComparer<T>)(LongEqualityComparer.Default);
-
- if (t.Equals(typeof(ulong)))
- return cachedDefault = (SCG.IEqualityComparer<T>)(ULongEqualityComparer.Default);
-
- if (t.Equals(typeof(float)))
- return cachedDefault = (SCG.IEqualityComparer<T>)(FloatEqualityComparer.Default);
-
- if (t.Equals(typeof(double)))
- return cachedDefault = (SCG.IEqualityComparer<T>)(DoubleEqualityComparer.Default);
-
- if (t.Equals(typeof(decimal)))
- return cachedDefault = (SCG.IEqualityComparer<T>)(DecimalEqualityComparer.Default);
- }
- Type[] interfaces = t.GetInterfaces();
- if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(isequenced))
- return createAndCache(orderedcollectionequalityComparer.MakeGenericType(new Type[] { t, t.GetGenericArguments()[0] }));
- foreach (Type ty in interfaces)
- {
- if (ty.IsGenericType && ty.GetGenericTypeDefinition().Equals(isequenced))
- return createAndCache(orderedcollectionequalityComparer.MakeGenericType(new Type[] { t, ty.GetGenericArguments()[0] }));
- }
- if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(icollection))
- return createAndCache(unorderedcollectionequalityComparer.MakeGenericType(new Type[] { t, t.GetGenericArguments()[0] }));
- foreach (Type ty in interfaces)
- {
- if (ty.IsGenericType && ty.GetGenericTypeDefinition().Equals(icollection))
- return createAndCache(unorderedcollectionequalityComparer.MakeGenericType(new Type[] { t, ty.GetGenericArguments()[0] }));
- }
- if (iequalitytype.IsAssignableFrom(t))
- return createAndCache(equalityequalityComparer.MakeGenericType(new Type[] { t }));
- else
- return cachedDefault = NaturalEqualityComparer<T>.Default;
- }
- }
- static SCG.IEqualityComparer<T> createAndCache(Type equalityComparertype)
- {
- return cachedDefault = (SCG.IEqualityComparer<T>)(equalityComparertype.GetProperty("Default", BindingFlags.Static | BindingFlags.Public).GetValue(null, null));
- }
- }
-
- /// <summary>
- /// A default item equalityComparer calling through to
- /// the GetHashCode and Equals methods inherited from System.Object.
- /// </summary>
- [Serializable]
- public sealed class NaturalEqualityComparer<T> : SCG.IEqualityComparer<T>
- {
- static NaturalEqualityComparer<T> cached;
- NaturalEqualityComparer() { }
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public static NaturalEqualityComparer<T> Default { get { return cached ?? (cached = new NaturalEqualityComparer<T>()); } }
- //TODO: check if null check is reasonable
- //Answer: if we have struct C<T> { T t; int i;} and implement GetHashCode as
- //the sum of hashcodes, and T may be any type, we cannot make the null check
- //inside the definition of C<T> in a reasonable way.
- /// <summary>
- /// Get the hash code with respect to this item equalityComparer
- /// </summary>
- /// <param name="item">The item</param>
- /// <returns>The hash code</returns>
- [Tested]
- public int GetHashCode(T item) { return item == null ? 0 : item.GetHashCode(); }
-
- /// <summary>
- /// Check if two items are equal with respect to this item equalityComparer
- /// </summary>
- /// <param name="item1">first item</param>
- /// <param name="item2">second item</param>
- /// <returns>True if equal</returns>
- [Tested]
- public bool Equals(T item1, T item2)
- {
- return item1 == null ? item2 == null : item1.Equals(item2);
- }
- }
-
- /// <summary>
- /// A default equality comparer for a type T that implements System.IEquatable<typeparamref name="T"/>.
- ///
- /// The equality comparer forwards calls to GetHashCode and Equals to the IEquatable methods
- /// on T, so Equals(T) is called, not Equals(object).
- /// This will save boxing abd unboxing if T is a value type
- /// and in general saves a runtime type check.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- [Serializable]
- public class EquatableEqualityComparer<T> : SCG.IEqualityComparer<T> where T : IEquatable<T>
- {
- static EquatableEqualityComparer<T> cached = new EquatableEqualityComparer<T>();
- EquatableEqualityComparer() { }
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public static EquatableEqualityComparer<T> Default {
- get { return cached ?? (cached = new EquatableEqualityComparer<T>()); }
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <returns></returns>
- public int GetHashCode(T item) { return item == null ? 0 : item.GetHashCode(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item1"></param>
- /// <param name="item2"></param>
- /// <returns></returns>
- public bool Equals(T item1, T item2) { return item1 == null ? item2 == null : item1.Equals(item2); }
- }
-
- /// <summary>
- /// A equalityComparer for a reference type that uses reference equality for equality and the hash code from object as hash code.
- /// </summary>
- /// <typeparam name="T">The item type. Must be a reference type.</typeparam>
- [Serializable]
- public class ReferenceEqualityComparer<T> : SCG.IEqualityComparer<T> where T : class
- {
- static ReferenceEqualityComparer<T> cached;
- ReferenceEqualityComparer() { }
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public static ReferenceEqualityComparer<T> Default {
- get { return cached ?? (cached = new ReferenceEqualityComparer<T>()); }
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <returns></returns>
- public int GetHashCode(T item)
- {
- return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(item);
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="i1"></param>
- /// <param name="i2"></param>
- /// <returns></returns>
- public bool Equals(T i1, T i2)
- {
- return object.ReferenceEquals(i1, i2);
- }
- }
-
- /// <summary>
- /// An equalityComparer compatible with a given comparer. All hash codes are 0,
- /// meaning that anything based on hash codes will be quite inefficient.
- /// <para><b>Note: this will give a new EqualityComparer each time created!</b></para>
- /// </summary>
- /// <typeparam name="T"></typeparam>
- [Serializable]
- public class ComparerZeroHashCodeEqualityComparer<T> : SCG.IEqualityComparer<T>
- {
- SCG.IComparer<T> comparer;
- /// <summary>
- /// Create a trivial <see cref="T:C5.IEqualityComparer`1"/> compatible with the
- /// <see cref="T:C5.IComparer`1"/> <code>comparer</code>
- /// </summary>
- /// <param name="comparer"></param>
- public ComparerZeroHashCodeEqualityComparer(SCG.IComparer<T> comparer)
- {
- if (comparer == null)
- throw new NullReferenceException("Comparer cannot be null");
- this.comparer = comparer;
- }
- /// <summary>
- /// A trivial, inefficient hash fuction. Compatible with any equality relation.
- /// </summary>
- /// <param name="item"></param>
- /// <returns>0</returns>
- public int GetHashCode(T item) { return 0; }
- /// <summary>
- /// Equality of two items as defined by the comparer.
- /// </summary>
- /// <param name="item1"></param>
- /// <param name="item2"></param>
- /// <returns></returns>
- public bool Equals(T item1, T item2) { return comparer.Compare(item1, item2) == 0; }
- }
-
- /// <summary>
- /// Prototype for a sequenced equalityComparer for something (T) that implements ISequenced[W].
- /// This will use ISequenced[W] specific implementations of the equality comparer operations.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <typeparam name="W"></typeparam>
- [Serializable]
- public class SequencedCollectionEqualityComparer<T, W> : SCG.IEqualityComparer<T>
- where T : ISequenced<W>
- {
- static SequencedCollectionEqualityComparer<T, W> cached;
- SequencedCollectionEqualityComparer() { }
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public static SequencedCollectionEqualityComparer<T, W> Default {
- get { return cached ?? (cached = new SequencedCollectionEqualityComparer<T, W>()); }
- }
- /// <summary>
- /// Get the hash code with respect to this sequenced equalityComparer
- /// </summary>
- /// <param name="collection">The collection</param>
- /// <returns>The hash code</returns>
- [Tested]
- public int GetHashCode(T collection) { return collection.GetSequencedHashCode(); }
-
- /// <summary>
- /// Check if two items are equal with respect to this sequenced equalityComparer
- /// </summary>
- /// <param name="collection1">first collection</param>
- /// <param name="collection2">second collection</param>
- /// <returns>True if equal</returns>
- [Tested]
- public bool Equals(T collection1, T collection2) { return collection1 == null ? collection2 == null : collection1.SequencedEquals(collection2); }
- }
-
- /// <summary>
- /// Prototype for an unsequenced equalityComparer for something (T) that implements ICollection[W]
- /// This will use ICollection[W] specific implementations of the equalityComparer operations
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <typeparam name="W"></typeparam>
- [Serializable]
- public class UnsequencedCollectionEqualityComparer<T, W> : SCG.IEqualityComparer<T>
- where T : ICollection<W>
- {
- static UnsequencedCollectionEqualityComparer<T, W> cached;
- UnsequencedCollectionEqualityComparer() { }
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public static UnsequencedCollectionEqualityComparer<T, W> Default { get { return cached ?? (cached = new UnsequencedCollectionEqualityComparer<T, W>()); } }
- /// <summary>
- /// Get the hash code with respect to this unsequenced equalityComparer
- /// </summary>
- /// <param name="collection">The collection</param>
- /// <returns>The hash code</returns>
- [Tested]
- public int GetHashCode(T collection) { return collection.GetUnsequencedHashCode(); }
-
-
- /// <summary>
- /// Check if two collections are equal with respect to this unsequenced equalityComparer
- /// </summary>
- /// <param name="collection1">first collection</param>
- /// <param name="collection2">second collection</param>
- /// <returns>True if equal</returns>
- [Tested]
- public bool Equals(T collection1, T collection2) { return collection1 == null ? collection2 == null : collection1.UnsequencedEquals(collection2); }
- }
-
-}
-
-
-#if EXPERIMENTAL
-namespace C5.EqualityComparerBuilder
-{
-
- /// <summary>
- /// IEqualityComparer factory class: examines at instatiation time if T is an
- /// interface implementing "int GetHashCode()" and "bool Equals(T)".
- /// If those are not present, MakeEqualityComparer will return a default equalityComparer,
- /// else this class will implement IequalityComparer[T] via Invoke() on the
- /// reflected method infos.
- /// </summary>
- public class ByInvoke<T> : SCG.IEqualityComparer<T>
- {
- internal static readonly System.Reflection.MethodInfo hinfo, einfo;
-
-
- static ByInvoke()
- {
- Type t = typeof(T);
-
- if (!t.IsInterface) return;
-
- BindingFlags f = BindingFlags.Public | BindingFlags.Instance;
-
- hinfo = t.GetMethod("GetHashCode", f, null, new Type[0], null);
- einfo = t.GetMethod("Equals", f, null, new Type[1] { t }, null);
- }
-
-
- private ByInvoke() { }
-
-/// <summary>
-///
-/// </summary>
-/// <returns></returns>
- public static SCG.IEqualityComparer<T> MakeEqualityComparer()
- {
- if (hinfo != null && einfo != null)
- return new ByInvoke<T>();
- else
- return NaturalEqualityComparer<T>.Default;
- }
-
-/// <summary>
-///
-/// </summary>
-/// <param name="item"></param>
-/// <returns></returns>
- public int GetHashCode(T item)
- {
- return (int)(hinfo.Invoke(item, null));
- }
-
-/// <summary>
-///
-/// </summary>
-/// <param name="i1"></param>
-/// <param name="i2"></param>
-/// <returns></returns>
- public bool Equals(T i1, T i2)
- {
- return (bool)(einfo.Invoke(i1, new object[1] { i2 }));
- }
- }
-
-
-
- /// <summary>
- /// Like ByInvoke, but tries to build a equalityComparer by RTCG to
- /// avoid the Invoke() overhead.
- /// </summary>
- public class ByRTCG
- {
- private static ModuleBuilder moduleBuilder;
-
- private static AssemblyBuilder assemblyBuilder;
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="hinfo"></param>
- /// <param name="einfo"></param>
- /// <returns></returns>
- public static SCG.IEqualityComparer<T> CreateEqualityComparer<T>(MethodInfo hinfo, MethodInfo einfo)
- {
- if (moduleBuilder == null)
- {
- string assmname = "LeFake";
- string filename = assmname + ".dll";
- AssemblyName assemblyName = new AssemblyName("LeFake");
- AppDomain appdomain = AppDomain.CurrentDomain;
- AssemblyBuilderAccess acc = AssemblyBuilderAccess.RunAndSave;
-
- assemblyBuilder = appdomain.DefineDynamicAssembly(assemblyName, acc);
- moduleBuilder = assemblyBuilder.DefineDynamicModule(assmname, filename);
- }
-
- Type t = typeof(T);
- Type o_t = typeof(object);
- Type h_t = typeof(SCG.IEqualityComparer<T>);
- Type i_t = typeof(int);
- //TODO: protect uid for thread safety!
- string name = "C5.Dynamic.EqualityComparer_" + Guid.NewGuid().ToString();
- TypeAttributes tatt = TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed;
- TypeBuilder tb = moduleBuilder.DefineType(name, tatt, o_t, new Type[1] { h_t });
- MethodAttributes matt = MethodAttributes.Public | MethodAttributes.Virtual;
- MethodBuilder mb = tb.DefineMethod("GetHashCode", matt, i_t, new Type[1] { t });
- ILGenerator ilg = mb.GetILGenerator();
-
- ilg.Emit(OpCodes.Ldarg_1);
- ilg.Emit(OpCodes.Callvirt, hinfo);
- ilg.Emit(OpCodes.Ret);
- mb = tb.DefineMethod("Equals", matt, typeof(bool), new Type[2] { t, t });
- ilg = mb.GetILGenerator();
- ilg.Emit(OpCodes.Ldarg_1);
- ilg.Emit(OpCodes.Ldarg_2);
- ilg.Emit(OpCodes.Callvirt, einfo);
- ilg.Emit(OpCodes.Ret);
-
- Type equalityComparer_t = tb.CreateType();
- object equalityComparer = equalityComparer_t.GetConstructor(new Type[0]).Invoke(null);
-
- return (SCG.IEqualityComparer<T>)equalityComparer;
- }
-
-/// <summary>
-///
-/// </summary>
-/// <typeparam name="T"></typeparam>
-/// <returns></returns>
- public static SCG.IEqualityComparer<T> build<T>()
- {
- MethodInfo hinfo = ByInvoke<T>.hinfo, einfo = ByInvoke<T>.einfo;
-
- if (hinfo != null && einfo != null)
- return CreateEqualityComparer<T>(hinfo, einfo);
- else
- return EqualityComparer<T>.Default;
- }
-
-/// <summary>
-///
-/// </summary>
- public void dump()
- {
- assemblyBuilder.Save("LeFake.dll");
- }
- }
-}
-#endif
+/* + Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +using C5; +using System; +using System.Reflection; +using System.Reflection.Emit; +using System.Diagnostics; +using SCG = System.Collections.Generic; + +namespace C5 +{ + /// <summary> + /// Utility class for building default generic equalityComparers. + /// </summary> + /// <typeparam name="T"></typeparam> + public static class EqualityComparer<T> + { + readonly static Type isequenced = typeof(ISequenced<>); + + readonly static Type icollection = typeof(ICollection<>); + + readonly static Type orderedcollectionequalityComparer = typeof(SequencedCollectionEqualityComparer<,>); + + readonly static Type unorderedcollectionequalityComparer = typeof(UnsequencedCollectionEqualityComparer<,>); + + readonly static Type equalityequalityComparer = typeof(EquatableEqualityComparer<>); + + readonly static Type iequalitytype = typeof(IEquatable<T>); + + static SCG.IEqualityComparer<T> cachedDefault = null; + + //TODO: find the right word for initialized+invocation + /// <summary> + /// A default generic equality comparer for type T. The procedure is as follows: + /// <list> + /// <item>If T is a primitive type (char, sbyte, byte, short, ushort, int, uint, float, double, decimal), + /// the equalityComparer will be a standard equalityComparer for that type</item> + /// <item>If the actual generic argument T implements the generic interface + /// <see cref="T:C5.ISequenced`1"/> for some value W of its generic parameter T, + /// the equalityComparer will be <see cref="T:C5.SequencedCollectionEqualityComparer`2"/></item> + /// <item>If the actual generic argument T implements + /// <see cref="T:C5.ICollection`1"/> for some value W of its generic parameter T, + /// the equalityComparer will be <see cref="T:C5.UnsequencedCollectionEqualityComparer`2"/></item> + /// <item>If T is a type implementing <see cref="T:C5.IEquatable`1"/>, the equalityComparer + /// will be <see cref="T:C5.EquatableEqualityComparer`1"/></item> + /// <item>If T is a type not implementing <see cref="T:C5.IEquatable`1"/>, the equalityComparer + /// will be <see cref="T:C5.NaturalEqualityComparer`1"/> </item> + /// </list> + /// The <see cref="T:C5.IEqualityComparer`1"/> object is constructed when this class is initialised, i.e. + /// its static constructors called. Thus, the property will be the same object + /// for the duration of an invocation of the runtime, but a value serialized in + /// another invocation and deserialized here will not be the same object. + /// </summary> + /// <value></value> + public static SCG.IEqualityComparer<T> Default + { + get + { + if (cachedDefault != null) + return cachedDefault; + + Type t = typeof(T); + + if (t.IsValueType) + { + if (t.Equals(typeof(char))) + return cachedDefault = (SCG.IEqualityComparer<T>)(CharEqualityComparer.Default); + + if (t.Equals(typeof(sbyte))) + return cachedDefault = (SCG.IEqualityComparer<T>)(SByteEqualityComparer.Default); + + if (t.Equals(typeof(byte))) + return cachedDefault = (SCG.IEqualityComparer<T>)(ByteEqualityComparer.Default); + + if (t.Equals(typeof(short))) + return cachedDefault = (SCG.IEqualityComparer<T>)(ShortEqualityComparer.Default); + + if (t.Equals(typeof(ushort))) + return cachedDefault = (SCG.IEqualityComparer<T>)(UShortEqualityComparer.Default); + + if (t.Equals(typeof(int))) + return cachedDefault = (SCG.IEqualityComparer<T>)(IntEqualityComparer.Default); + + if (t.Equals(typeof(uint))) + return cachedDefault = (SCG.IEqualityComparer<T>)(UIntEqualityComparer.Default); + + if (t.Equals(typeof(long))) + return cachedDefault = (SCG.IEqualityComparer<T>)(LongEqualityComparer.Default); + + if (t.Equals(typeof(ulong))) + return cachedDefault = (SCG.IEqualityComparer<T>)(ULongEqualityComparer.Default); + + if (t.Equals(typeof(float))) + return cachedDefault = (SCG.IEqualityComparer<T>)(FloatEqualityComparer.Default); + + if (t.Equals(typeof(double))) + return cachedDefault = (SCG.IEqualityComparer<T>)(DoubleEqualityComparer.Default); + + if (t.Equals(typeof(decimal))) + return cachedDefault = (SCG.IEqualityComparer<T>)(DecimalEqualityComparer.Default); + } + Type[] interfaces = t.GetInterfaces(); + if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(isequenced)) + return createAndCache(orderedcollectionequalityComparer.MakeGenericType(new Type[] { t, t.GetGenericArguments()[0] })); + foreach (Type ty in interfaces) + { + if (ty.IsGenericType && ty.GetGenericTypeDefinition().Equals(isequenced)) + return createAndCache(orderedcollectionequalityComparer.MakeGenericType(new Type[] { t, ty.GetGenericArguments()[0] })); + } + if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(icollection)) + return createAndCache(unorderedcollectionequalityComparer.MakeGenericType(new Type[] { t, t.GetGenericArguments()[0] })); + foreach (Type ty in interfaces) + { + if (ty.IsGenericType && ty.GetGenericTypeDefinition().Equals(icollection)) + return createAndCache(unorderedcollectionequalityComparer.MakeGenericType(new Type[] { t, ty.GetGenericArguments()[0] })); + } + if (iequalitytype.IsAssignableFrom(t)) + return createAndCache(equalityequalityComparer.MakeGenericType(new Type[] { t })); + else + return cachedDefault = NaturalEqualityComparer<T>.Default; + } + } + static SCG.IEqualityComparer<T> createAndCache(Type equalityComparertype) + { + return cachedDefault = (SCG.IEqualityComparer<T>)(equalityComparertype.GetProperty("Default", BindingFlags.Static | BindingFlags.Public).GetValue(null, null)); + } + } + + /// <summary> + /// A default item equalityComparer calling through to + /// the GetHashCode and Equals methods inherited from System.Object. + /// </summary> + [Serializable] + public sealed class NaturalEqualityComparer<T> : SCG.IEqualityComparer<T> + { + static NaturalEqualityComparer<T> cached; + NaturalEqualityComparer() { } + /// <summary> + /// + /// </summary> + /// <value></value> + public static NaturalEqualityComparer<T> Default { get { return cached ?? (cached = new NaturalEqualityComparer<T>()); } } + //TODO: check if null check is reasonable + //Answer: if we have struct C<T> { T t; int i;} and implement GetHashCode as + //the sum of hashcodes, and T may be any type, we cannot make the null check + //inside the definition of C<T> in a reasonable way. + /// <summary> + /// Get the hash code with respect to this item equalityComparer + /// </summary> + /// <param name="item">The item</param> + /// <returns>The hash code</returns> + [Tested] + public int GetHashCode(T item) { return item == null ? 0 : item.GetHashCode(); } + + /// <summary> + /// Check if two items are equal with respect to this item equalityComparer + /// </summary> + /// <param name="item1">first item</param> + /// <param name="item2">second item</param> + /// <returns>True if equal</returns> + [Tested] + public bool Equals(T item1, T item2) + { + return item1 == null ? item2 == null : item1.Equals(item2); + } + } + + /// <summary> + /// A default equality comparer for a type T that implements System.IEquatable<typeparamref name="T"/>. + /// + /// The equality comparer forwards calls to GetHashCode and Equals to the IEquatable methods + /// on T, so Equals(T) is called, not Equals(object). + /// This will save boxing abd unboxing if T is a value type + /// and in general saves a runtime type check. + /// </summary> + /// <typeparam name="T"></typeparam> + [Serializable] + public class EquatableEqualityComparer<T> : SCG.IEqualityComparer<T> where T : IEquatable<T> + { + static EquatableEqualityComparer<T> cached = new EquatableEqualityComparer<T>(); + EquatableEqualityComparer() { } + /// <summary> + /// + /// </summary> + /// <value></value> + public static EquatableEqualityComparer<T> Default { + get { return cached ?? (cached = new EquatableEqualityComparer<T>()); } + } + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <returns></returns> + public int GetHashCode(T item) { return item == null ? 0 : item.GetHashCode(); } + + /// <summary> + /// + /// </summary> + /// <param name="item1"></param> + /// <param name="item2"></param> + /// <returns></returns> + public bool Equals(T item1, T item2) { return item1 == null ? item2 == null : item1.Equals(item2); } + } + + /// <summary> + /// A equalityComparer for a reference type that uses reference equality for equality and the hash code from object as hash code. + /// </summary> + /// <typeparam name="T">The item type. Must be a reference type.</typeparam> + [Serializable] + public class ReferenceEqualityComparer<T> : SCG.IEqualityComparer<T> where T : class + { + static ReferenceEqualityComparer<T> cached; + ReferenceEqualityComparer() { } + /// <summary> + /// + /// </summary> + /// <value></value> + public static ReferenceEqualityComparer<T> Default { + get { return cached ?? (cached = new ReferenceEqualityComparer<T>()); } + } + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <returns></returns> + public int GetHashCode(T item) + { + return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(item); + } + + /// <summary> + /// + /// </summary> + /// <param name="i1"></param> + /// <param name="i2"></param> + /// <returns></returns> + public bool Equals(T i1, T i2) + { + return object.ReferenceEquals(i1, i2); + } + } + + /// <summary> + /// An equalityComparer compatible with a given comparer. All hash codes are 0, + /// meaning that anything based on hash codes will be quite inefficient. + /// <para><b>Note: this will give a new EqualityComparer each time created!</b></para> + /// </summary> + /// <typeparam name="T"></typeparam> + [Serializable] + public class ComparerZeroHashCodeEqualityComparer<T> : SCG.IEqualityComparer<T> + { + SCG.IComparer<T> comparer; + /// <summary> + /// Create a trivial <see cref="T:C5.IEqualityComparer`1"/> compatible with the + /// <see cref="T:C5.IComparer`1"/> <code>comparer</code> + /// </summary> + /// <param name="comparer"></param> + public ComparerZeroHashCodeEqualityComparer(SCG.IComparer<T> comparer) + { + if (comparer == null) + throw new NullReferenceException("Comparer cannot be null"); + this.comparer = comparer; + } + /// <summary> + /// A trivial, inefficient hash fuction. Compatible with any equality relation. + /// </summary> + /// <param name="item"></param> + /// <returns>0</returns> + public int GetHashCode(T item) { return 0; } + /// <summary> + /// Equality of two items as defined by the comparer. + /// </summary> + /// <param name="item1"></param> + /// <param name="item2"></param> + /// <returns></returns> + public bool Equals(T item1, T item2) { return comparer.Compare(item1, item2) == 0; } + } + + /// <summary> + /// Prototype for a sequenced equalityComparer for something (T) that implements ISequenced[W]. + /// This will use ISequenced[W] specific implementations of the equality comparer operations. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <typeparam name="W"></typeparam> + [Serializable] + public class SequencedCollectionEqualityComparer<T, W> : SCG.IEqualityComparer<T> + where T : ISequenced<W> + { + static SequencedCollectionEqualityComparer<T, W> cached; + SequencedCollectionEqualityComparer() { } + /// <summary> + /// + /// </summary> + /// <value></value> + public static SequencedCollectionEqualityComparer<T, W> Default { + get { return cached ?? (cached = new SequencedCollectionEqualityComparer<T, W>()); } + } + /// <summary> + /// Get the hash code with respect to this sequenced equalityComparer + /// </summary> + /// <param name="collection">The collection</param> + /// <returns>The hash code</returns> + [Tested] + public int GetHashCode(T collection) { return collection.GetSequencedHashCode(); } + + /// <summary> + /// Check if two items are equal with respect to this sequenced equalityComparer + /// </summary> + /// <param name="collection1">first collection</param> + /// <param name="collection2">second collection</param> + /// <returns>True if equal</returns> + [Tested] + public bool Equals(T collection1, T collection2) { return collection1 == null ? collection2 == null : collection1.SequencedEquals(collection2); } + } + + /// <summary> + /// Prototype for an unsequenced equalityComparer for something (T) that implements ICollection[W] + /// This will use ICollection[W] specific implementations of the equalityComparer operations + /// </summary> + /// <typeparam name="T"></typeparam> + /// <typeparam name="W"></typeparam> + [Serializable] + public class UnsequencedCollectionEqualityComparer<T, W> : SCG.IEqualityComparer<T> + where T : ICollection<W> + { + static UnsequencedCollectionEqualityComparer<T, W> cached; + UnsequencedCollectionEqualityComparer() { } + /// <summary> + /// + /// </summary> + /// <value></value> + public static UnsequencedCollectionEqualityComparer<T, W> Default { get { return cached ?? (cached = new UnsequencedCollectionEqualityComparer<T, W>()); } } + /// <summary> + /// Get the hash code with respect to this unsequenced equalityComparer + /// </summary> + /// <param name="collection">The collection</param> + /// <returns>The hash code</returns> + [Tested] + public int GetHashCode(T collection) { return collection.GetUnsequencedHashCode(); } + + + /// <summary> + /// Check if two collections are equal with respect to this unsequenced equalityComparer + /// </summary> + /// <param name="collection1">first collection</param> + /// <param name="collection2">second collection</param> + /// <returns>True if equal</returns> + [Tested] + public bool Equals(T collection1, T collection2) { return collection1 == null ? collection2 == null : collection1.UnsequencedEquals(collection2); } + } + +} + + +#if EXPERIMENTAL +namespace C5.EqualityComparerBuilder +{ + + /// <summary> + /// IEqualityComparer factory class: examines at instatiation time if T is an + /// interface implementing "int GetHashCode()" and "bool Equals(T)". + /// If those are not present, MakeEqualityComparer will return a default equalityComparer, + /// else this class will implement IequalityComparer[T] via Invoke() on the + /// reflected method infos. + /// </summary> + public class ByInvoke<T> : SCG.IEqualityComparer<T> + { + internal static readonly System.Reflection.MethodInfo hinfo, einfo; + + + static ByInvoke() + { + Type t = typeof(T); + + if (!t.IsInterface) return; + + BindingFlags f = BindingFlags.Public | BindingFlags.Instance; + + hinfo = t.GetMethod("GetHashCode", f, null, new Type[0], null); + einfo = t.GetMethod("Equals", f, null, new Type[1] { t }, null); + } + + + private ByInvoke() { } + +/// <summary> +/// +/// </summary> +/// <returns></returns> + public static SCG.IEqualityComparer<T> MakeEqualityComparer() + { + if (hinfo != null && einfo != null) + return new ByInvoke<T>(); + else + return NaturalEqualityComparer<T>.Default; + } + +/// <summary> +/// +/// </summary> +/// <param name="item"></param> +/// <returns></returns> + public int GetHashCode(T item) + { + return (int)(hinfo.Invoke(item, null)); + } + +/// <summary> +/// +/// </summary> +/// <param name="i1"></param> +/// <param name="i2"></param> +/// <returns></returns> + public bool Equals(T i1, T i2) + { + return (bool)(einfo.Invoke(i1, new object[1] { i2 })); + } + } + + + + /// <summary> + /// Like ByInvoke, but tries to build a equalityComparer by RTCG to + /// avoid the Invoke() overhead. + /// </summary> + public class ByRTCG + { + private static ModuleBuilder moduleBuilder; + + private static AssemblyBuilder assemblyBuilder; + + /// <summary> + /// + /// </summary> + /// <param name="hinfo"></param> + /// <param name="einfo"></param> + /// <returns></returns> + public static SCG.IEqualityComparer<T> CreateEqualityComparer<T>(MethodInfo hinfo, MethodInfo einfo) + { + if (moduleBuilder == null) + { + string assmname = "LeFake"; + string filename = assmname + ".dll"; + AssemblyName assemblyName = new AssemblyName("LeFake"); + AppDomain appdomain = AppDomain.CurrentDomain; + AssemblyBuilderAccess acc = AssemblyBuilderAccess.RunAndSave; + + assemblyBuilder = appdomain.DefineDynamicAssembly(assemblyName, acc); + moduleBuilder = assemblyBuilder.DefineDynamicModule(assmname, filename); + } + + Type t = typeof(T); + Type o_t = typeof(object); + Type h_t = typeof(SCG.IEqualityComparer<T>); + Type i_t = typeof(int); + //TODO: protect uid for thread safety! + string name = "C5.Dynamic.EqualityComparer_" + Guid.NewGuid().ToString(); + TypeAttributes tatt = TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed; + TypeBuilder tb = moduleBuilder.DefineType(name, tatt, o_t, new Type[1] { h_t }); + MethodAttributes matt = MethodAttributes.Public | MethodAttributes.Virtual; + MethodBuilder mb = tb.DefineMethod("GetHashCode", matt, i_t, new Type[1] { t }); + ILGenerator ilg = mb.GetILGenerator(); + + ilg.Emit(OpCodes.Ldarg_1); + ilg.Emit(OpCodes.Callvirt, hinfo); + ilg.Emit(OpCodes.Ret); + mb = tb.DefineMethod("Equals", matt, typeof(bool), new Type[2] { t, t }); + ilg = mb.GetILGenerator(); + ilg.Emit(OpCodes.Ldarg_1); + ilg.Emit(OpCodes.Ldarg_2); + ilg.Emit(OpCodes.Callvirt, einfo); + ilg.Emit(OpCodes.Ret); + + Type equalityComparer_t = tb.CreateType(); + object equalityComparer = equalityComparer_t.GetConstructor(new Type[0]).Invoke(null); + + return (SCG.IEqualityComparer<T>)equalityComparer; + } + +/// <summary> +/// +/// </summary> +/// <typeparam name="T"></typeparam> +/// <returns></returns> + public static SCG.IEqualityComparer<T> build<T>() + { + MethodInfo hinfo = ByInvoke<T>.hinfo, einfo = ByInvoke<T>.einfo; + + if (hinfo != null && einfo != null) + return CreateEqualityComparer<T>(hinfo, einfo); + else + return EqualityComparer<T>.Default; + } + +/// <summary> +/// +/// </summary> + public void dump() + { + assemblyBuilder.Save("LeFake.dll"); + } + } +} +#endif diff --git a/mcs/class/Mono.C5/C5/Interfaces.cs b/mcs/class/Mono.C5/C5/Interfaces.cs index e6c7a10564c..578af96e976 100644 --- a/mcs/class/Mono.C5/C5/Interfaces.cs +++ b/mcs/class/Mono.C5/C5/Interfaces.cs @@ -1,2059 +1,2059 @@ -/*
- Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-
-using System;
-using SCG = System.Collections.Generic;
-namespace C5
-{
- /// <summary>
- /// A generic collection, that can be enumerated backwards.
- /// </summary>
- public interface IDirectedEnumerable<T> : SCG.IEnumerable<T>
- {
- /// <summary>
- /// Create a collection containing the same items as this collection, but
- /// whose enumerator will enumerate the items backwards. The new collection
- /// will become invalid if the original is modified. Method typically used as in
- /// <code>foreach (T x in coll.Backwards()) {...}</code>
- /// </summary>
- /// <returns>The backwards collection.</returns>
- IDirectedEnumerable<T> Backwards();
-
-
- /// <summary>
- /// <code>Forwards</code> if same, else <code>Backwards</code>
- /// </summary>
- /// <value>The enumeration direction relative to the original collection.</value>
- EnumerationDirection Direction { get;}
- }
-
- /// <summary>
- /// A generic collection that may be enumerated and can answer
- /// efficiently how many items it contains. Like <code>IEnumerable<T></code>,
- /// this interface does not prescribe any operations to initialize or update the
- /// collection. The main usage for this interface is to be the return type of
- /// query operations on generic collection.
- /// </summary>
- public interface ICollectionValue<T> : SCG.IEnumerable<T>, IShowable
- {
- /// <summary>
- /// A flag bitmap of the events subscribable to by this collection.
- /// </summary>
- /// <value></value>
- EventTypeEnum ListenableEvents { get;}
-
- /// <summary>
- /// A flag bitmap of the events currently subscribed to by this collection.
- /// </summary>
- /// <value></value>
- EventTypeEnum ActiveEvents { get;}
-
- /// <summary>
- /// The change event. Will be raised for every change operation on the collection.
- /// </summary>
- event CollectionChangedHandler<T> CollectionChanged;
-
- /// <summary>
- /// The change event. Will be raised for every clear operation on the collection.
- /// </summary>
- event CollectionClearedHandler<T> CollectionCleared;
-
- /// <summary>
- /// The item added event. Will be raised for every individual addition to the collection.
- /// </summary>
- event ItemsAddedHandler<T> ItemsAdded;
-
- /// <summary>
- /// The item inserted event. Will be raised for every individual insertion to the collection.
- /// </summary>
- event ItemInsertedHandler<T> ItemInserted;
-
- /// <summary>
- /// The item removed event. Will be raised for every individual removal from the collection.
- /// </summary>
- event ItemsRemovedHandler<T> ItemsRemoved;
-
- /// <summary>
- /// The item removed at event. Will be raised for every individual removal at from the collection.
- /// </summary>
- event ItemRemovedAtHandler<T> ItemRemovedAt;
-
- /// <summary>
- ///
- /// </summary>
- /// <value>True if this collection is empty.</value>
- bool IsEmpty { get;}
-
- /// <summary>
- /// </summary>
- /// <value>The number of items in this collection</value>
- int Count { get;}
-
- /// <summary>
- /// The value is symbolic indicating the type of asymptotic complexity
- /// in terms of the size of this collection (worst-case or amortized as
- /// relevant).
- /// </summary>
- /// <value>A characterization of the speed of the
- /// <code>Count</code> property in this collection.</value>
- Speed CountSpeed { get;}
-
- /// <summary>
- /// Copy the items of this collection to a contiguous part of an array.
- /// </summary>
- /// <param name="array">The array to copy to</param>
- /// <param name="index">The index at which to copy the first item</param>
- void CopyTo(T[] array, int index);
-
- /// <summary>
- /// Create an array with the items of this collection (in the same order as an
- /// enumerator would output them).
- /// </summary>
- /// <returns>The array</returns>
- T[] ToArray();
-
- /// <summary>
- /// Apply a delegate to all items of this collection.
- /// </summary>
- /// <param name="action">The delegate to apply</param>
- void Apply(Act<T> action);
-
-
- /// <summary>
- /// Check if there exists an item that satisfies a
- /// specific predicate in this collection.
- /// </summary>
- /// <param name="predicate">A delegate
- /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param>
- /// <returns>True is such an item exists</returns>
- bool Exists(Fun<T, bool> predicate);
-
- /// <summary>
- /// Check if there exists an item that satisfies a
- /// specific predicate in this collection and return the first one in enumeration order.
- /// </summary>
- /// <param name="predicate">A delegate
- /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param>
- /// <param name="item"></param>
- /// <returns>True is such an item exists</returns>
- bool Find(Fun<T, bool> predicate, out T item);
-
-
- /// <summary>
- /// Check if all items in this collection satisfies a specific predicate.
- /// </summary>
- /// <param name="predicate">A delegate
- /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param>
- /// <returns>True if all items satisfies the predicate</returns>
- bool All(Fun<T, bool> predicate);
-
- /// <summary>
- /// Choose some item of this collection.
- /// <para>Implementations must assure that the item
- /// returned may be efficiently removed.</para>
- /// <para>Implementors may decide to implement this method in a way such that repeated
- /// calls do not necessarily give the same result, i.e. so that the result of the following
- /// test is undetermined:
- /// <code>coll.Choose() == coll.Choose()</code></para>
- /// </summary>
- /// <exception cref="NoSuchItemException">if collection is empty.</exception>
- /// <returns></returns>
- T Choose();
-
- /// <summary>
- /// Create an enumerable, enumerating the items of this collection that satisfies
- /// a certain condition.
- /// </summary>
- /// <param name="filter">The T->bool filter delegate defining the condition</param>
- /// <returns>The filtered enumerable</returns>
- SCG.IEnumerable<T> Filter(Fun<T, bool> filter);
- }
-
-
-
- /// <summary>
- /// A sized generic collection, that can be enumerated backwards.
- /// </summary>
- public interface IDirectedCollectionValue<T> : ICollectionValue<T>, IDirectedEnumerable<T>
- {
- /// <summary>
- /// Create a collection containing the same items as this collection, but
- /// whose enumerator will enumerate the items backwards. The new collection
- /// will become invalid if the original is modified. Method typically used as in
- /// <code>foreach (T x in coll.Backwards()) {...}</code>
- /// </summary>
- /// <returns>The backwards collection.</returns>
- new IDirectedCollectionValue<T> Backwards();
-
- /// <summary>
- /// Check if there exists an item that satisfies a
- /// specific predicate in this collection and return the first one in enumeration order.
- /// </summary>
- /// <param name="predicate">A delegate
- /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param>
- /// <param name="item"></param>
- /// <returns>True is such an item exists</returns>
- bool FindLast(Fun<T, bool> predicate, out T item);
- }
-
-
- /// <summary>
- /// A generic collection to which one may add items. This is just the intersection
- /// of the main stream generic collection interfaces and the priority queue interface,
- /// <see cref="T:C5.ICollection`1"/> and <see cref="T:C5.IPriorityQueue`1"/>.
- /// </summary>
- public interface IExtensible<T> : ICollectionValue<T>, ICloneable
- {
- /// <summary>
- /// If true any call of an updating operation will throw an
- /// <code>ReadOnlyCollectionException</code>
- /// </summary>
- /// <value>True if this collection is read-only.</value>
- bool IsReadOnly { get;}
-
- //TODO: wonder where the right position of this is
- /// <summary>
- ///
- /// </summary>
- /// <value>False if this collection has set semantics, true if bag semantics.</value>
- bool AllowsDuplicates { get;}
-
- //TODO: wonder where the right position of this is. And the semantics.
- /// <summary>
- /// (Here should be a discussion of the role of equalityComparers. Any ).
- /// </summary>
- /// <value>The equalityComparer used by this collection to check equality of items.
- /// Or null (????) if collection does not check equality at all or uses a comparer.</value>
- SCG.IEqualityComparer<T> EqualityComparer { get;}
-
- //ItemEqualityTypeEnum ItemEqualityType {get ;}
-
- //TODO: find a good name
-
- /// <summary>
- /// By convention this is true for any collection with set semantics.
- /// </summary>
- /// <value>True if only one representative of a group of equal items
- /// is kept in the collection together with the total count.</value>
- bool DuplicatesByCounting { get;}
-
- /// <summary>
- /// Add an item to this collection if possible. If this collection has set
- /// semantics, the item will be added if not already in the collection. If
- /// bag semantics, the item will always be added.
- /// </summary>
- /// <param name="item">The item to add.</param>
- /// <returns>True if item was added.</returns>
- bool Add(T item);
-
- /// <summary>
- /// Add the elements from another collection with a more specialized item type
- /// to this collection. If this
- /// collection has set semantics, only items not already in the collection
- /// will be added.
- /// </summary>
- /// <typeparam name="U">The type of items to add</typeparam>
- /// <param name="items">The items to add</param>
- void AddAll<U>(SCG.IEnumerable<U> items) where U : T;
-
- //void Clear(); // for priority queue
- //int Count why not?
- /// <summary>
- /// Check the integrity of the internal data structures of this collection.
- /// <i>This is only relevant for developers of the library</i>
- /// </summary>
- /// <returns>True if check was passed.</returns>
- bool Check();
- }
-
- /// <summary>
- /// The simplest interface of a main stream generic collection
- /// with lookup, insertion and removal operations.
- /// </summary>
- public interface ICollection<T> : IExtensible<T>, SCG.ICollection<T>
- {
- //This is somewhat similar to the RandomAccess marker itf in java
- /// <summary>
- /// The value is symbolic indicating the type of asymptotic complexity
- /// in terms of the size of this collection (worst-case or amortized as
- /// relevant).
- /// <para>See <see cref="T:C5.Speed"/> for the set of symbols.</para>
- /// </summary>
- /// <value>A characterization of the speed of lookup operations
- /// (<code>Contains()</code> etc.) of the implementation of this collection.</value>
- Speed ContainsSpeed { get;}
-
- /// <summary>
- /// </summary>
- /// <value>The number of items in this collection</value>
- new int Count { get; }
-
- /// <summary>
- /// If true any call of an updating operation will throw an
- /// <code>ReadOnlyCollectionException</code>
- /// </summary>
- /// <value>True if this collection is read-only.</value>
- new bool IsReadOnly { get; }
-
- /// <summary>
- /// Add an item to this collection if possible. If this collection has set
- /// semantics, the item will be added if not already in the collection. If
- /// bag semantics, the item will always be added.
- /// </summary>
- /// <param name="item">The item to add.</param>
- /// <returns>True if item was added.</returns>
- new bool Add(T item);
-
- /// <summary>
- /// Copy the items of this collection to a contiguous part of an array.
- /// </summary>
- /// <param name="array">The array to copy to</param>
- /// <param name="index">The index at which to copy the first item</param>
- new void CopyTo(T[] array, int index);
-
- /// <summary>
- /// The unordered collection hashcode is defined as the sum of
- /// <code>h(hashcode(item))</code> over the items
- /// of the collection, where the function <code>h</code> is a function from
- /// int to int of the form <code> t -> (a0*t+b0)^(a1*t+b1)^(a2*t+b2)</code>, where
- /// the ax and bx are the same for all collection classes.
- /// <para>The current implementation uses fixed values for the ax and bx,
- /// specified as constants in the code.</para>
- /// </summary>
- /// <returns>The unordered hashcode of this collection.</returns>
- int GetUnsequencedHashCode();
-
-
- /// <summary>
- /// Compare the contents of this collection to another one without regards to
- /// the sequence order. The comparison will use this collection's itemequalityComparer
- /// to compare individual items.
- /// </summary>
- /// <param name="otherCollection">The collection to compare to.</param>
- /// <returns>True if this collection and that contains the same items.</returns>
- bool UnsequencedEquals(ICollection<T> otherCollection);
-
-
- /// <summary>
- /// Check if this collection contains (an item equivalent to according to the
- /// itemequalityComparer) a particular value.
- /// </summary>
- /// <param name="item">The value to check for.</param>
- /// <returns>True if the items is in this collection.</returns>
- new bool Contains(T item);
-
-
- /// <summary>
- /// Count the number of items of the collection equal to a particular value.
- /// Returns 0 if and only if the value is not in the collection.
- /// </summary>
- /// <param name="item">The value to count.</param>
- /// <returns>The number of copies found.</returns>
- int ContainsCount(T item);
-
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- ICollectionValue<T> UniqueItems();
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- ICollectionValue<KeyValuePair<T, int>> ItemMultiplicities();
-
- /// <summary>
- /// Check whether this collection contains all the values in another collection.
- /// If this collection has bag semantics (<code>AllowsDuplicates==true</code>)
- /// the check is made with respect to multiplicities, else multiplicities
- /// are not taken into account.
- /// </summary>
- /// <param name="items">The </param>
- /// <typeparam name="U"></typeparam>
- /// <returns>True if all values in <code>items</code>is in this collection.</returns>
- bool ContainsAll<U>(SCG.IEnumerable<U> items) where U : T;
-
-
- /// <summary>
- /// Check if this collection contains an item equivalent according to the
- /// itemequalityComparer to a particular value. If so, return in the ref argument (a
- /// binary copy of) the actual value found.
- /// </summary>
- /// <param name="item">The value to look for.</param>
- /// <returns>True if the items is in this collection.</returns>
- bool Find(ref T item);
-
-
- //This should probably just be bool Add(ref T item); !!!
- /// <summary>
- /// Check if this collection contains an item equivalent according to the
- /// itemequalityComparer to a particular value. If so, return in the ref argument (a
- /// binary copy of) the actual value found. Else, add the item to the collection.
- /// </summary>
- /// <param name="item">The value to look for.</param>
- /// <returns>True if the item was found (hence not added).</returns>
- bool FindOrAdd(ref T item);
-
-
- /// <summary>
- /// Check if this collection contains an item equivalent according to the
- /// itemequalityComparer to a particular value. If so, update the item in the collection
- /// with a (binary copy of) the supplied value. If the collection has bag semantics,
- /// it depends on the value of DuplicatesByCounting if this updates all equivalent copies in
- /// the collection or just one.
- /// </summary>
- /// <param name="item">Value to update.</param>
- /// <returns>True if the item was found and hence updated.</returns>
- bool Update(T item);
-
- /// <summary>
- /// Check if this collection contains an item equivalent according to the
- /// itemequalityComparer to a particular value. If so, update the item in the collection
- /// with a (binary copy of) the supplied value. If the collection has bag semantics,
- /// it depends on the value of DuplicatesByCounting if this updates all equivalent copies in
- /// the collection or just one.
- /// </summary>
- /// <param name="item">Value to update.</param>
- /// <param name="olditem">On output the olditem, if found.</param>
- /// <returns>True if the item was found and hence updated.</returns>
- bool Update(T item, out T olditem);
-
-
- /// <summary>
- /// Check if this collection contains an item equivalent according to the
- /// itemequalityComparer to a particular value. If so, update the item in the collection
- /// to with a binary copy of the supplied value; else add the value to the collection.
- /// </summary>
- /// <param name="item">Value to add or update.</param>
- /// <returns>True if the item was found and updated (hence not added).</returns>
- bool UpdateOrAdd(T item);
-
-
- /// <summary>
- /// Check if this collection contains an item equivalent according to the
- /// itemequalityComparer to a particular value. If so, update the item in the collection
- /// to with a binary copy of the supplied value; else add the value to the collection.
- /// </summary>
- /// <param name="item">Value to add or update.</param>
- /// <param name="olditem">On output the olditem, if found.</param>
- /// <returns>True if the item was found and updated (hence not added).</returns>
- bool UpdateOrAdd(T item, out T olditem);
-
- /// <summary>
- /// Remove a particular item from this collection. If the collection has bag
- /// semantics only one copy equivalent to the supplied item is removed.
- /// </summary>
- /// <param name="item">The value to remove.</param>
- /// <returns>True if the item was found (and removed).</returns>
- new bool Remove(T item);
-
-
- /// <summary>
- /// Remove a particular item from this collection if found. If the collection
- /// has bag semantics only one copy equivalent to the supplied item is removed,
- /// which one is implementation dependent.
- /// If an item was removed, report a binary copy of the actual item removed in
- /// the argument.
- /// </summary>
- /// <param name="item">The value to remove.</param>
- /// <param name="removeditem">The value removed if any.</param>
- /// <returns>True if the item was found (and removed).</returns>
- bool Remove(T item, out T removeditem);
-
-
- /// <summary>
- /// Remove all items equivalent to a given value.
- /// </summary>
- /// <param name="item">The value to remove.</param>
- void RemoveAllCopies(T item);
-
-
- /// <summary>
- /// Remove all items in another collection from this one. If this collection
- /// has bag semantics, take multiplicities into account.
- /// </summary>
- /// <typeparam name="U"></typeparam>
- /// <param name="items">The items to remove.</param>
- void RemoveAll<U>(SCG.IEnumerable<U> items) where U : T;
-
- //void RemoveAll(Fun<T, bool> predicate);
-
- /// <summary>
- /// Remove all items from this collection.
- /// </summary>
- new void Clear();
-
-
- /// <summary>
- /// Remove all items not in some other collection from this one. If this collection
- /// has bag semantics, take multiplicities into account.
- /// </summary>
- /// <typeparam name="U"></typeparam>
- /// <param name="items">The items to retain.</param>
- void RetainAll<U>(SCG.IEnumerable<U> items) where U : T;
-
- //void RetainAll(Fun<T, bool> predicate);
- //IDictionary<T> UniqueItems()
- }
-
-
-
- /// <summary>
- /// An editable collection maintaining a definite sequence order of the items.
- ///
- /// <i>Implementations of this interface must compute the hash code and
- /// equality exactly as prescribed in the method definitions in order to
- /// be consistent with other collection classes implementing this interface.</i>
- /// <i>This interface is usually implemented by explicit interface implementation,
- /// not as ordinary virtual methods.</i>
- /// </summary>
- public interface ISequenced<T> : ICollection<T>, IDirectedCollectionValue<T>
- {
- /// <summary>
- /// The hashcode is defined as <code>h(...h(h(h(x1),x2),x3),...,xn)</code> for
- /// <code>h(a,b)=CONSTANT*a+b</code> and the x's the hash codes of the items of
- /// this collection.
- /// </summary>
- /// <returns>The sequence order hashcode of this collection.</returns>
- int GetSequencedHashCode();
-
-
- /// <summary>
- /// Compare this sequenced collection to another one in sequence order.
- /// </summary>
- /// <param name="otherCollection">The sequenced collection to compare to.</param>
- /// <returns>True if this collection and that contains equal (according to
- /// this collection's itemequalityComparer) in the same sequence order.</returns>
- bool SequencedEquals(ISequenced<T> otherCollection);
- }
-
-
-
- /// <summary>
- /// A sequenced collection, where indices of items in the order are maintained
- /// </summary>
- public interface IIndexed<T> : ISequenced<T>
- {
- /// <summary>
- /// </summary>
- /// <exception cref="IndexOutOfRangeException"> if <code>index</code> is negative or
- /// >= the size of the collection.</exception>
- /// <value>The <code>index</code>'th item of this list.</value>
- /// <param name="index">the index to lookup</param>
- T this[int index] { get;}
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- Speed IndexingSpeed { get;}
-
- /// <summary>
- /// </summary>
- /// <exception cref="ArgumentOutOfRangeException"></exception>
- /// <value>The directed collection of items in a specific index interval.</value>
- /// <param name="start">The low index of the interval (inclusive).</param>
- /// <param name="count">The size of the range.</param>
- IDirectedCollectionValue<T> this[int start, int count] { get;}
-
-
- /// <summary>
- /// Searches for an item in the list going forwards from the start.
- /// </summary>
- /// <param name="item">Item to search for.</param>
- /// <returns>Index of item from start. A negative number if item not found,
- /// namely the one's complement of the index at which the Add operation would put the item.</returns>
- int IndexOf(T item);
-
-
- /// <summary>
- /// Searches for an item in the list going backwards from the end.
- /// </summary>
- /// <param name="item">Item to search for.</param>
- /// <returns>Index of of item from the end. A negative number if item not found,
- /// namely the two-complement of the index at which the Add operation would put the item.</returns>
- int LastIndexOf(T item);
-
- /// <summary>
- /// Check if there exists an item that satisfies a
- /// specific predicate in this collection and return the index of the first one.
- /// </summary>
- /// <param name="predicate">A delegate
- /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param>
- /// <returns>the index, if found, a negative value else</returns>
- int FindIndex(Fun<T, bool> predicate);
-
- /// <summary>
- /// Check if there exists an item that satisfies a
- /// specific predicate in this collection and return the index of the last one.
- /// </summary>
- /// <param name="predicate">A delegate
- /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param>
- /// <returns>the index, if found, a negative value else</returns>
- int FindLastIndex(Fun<T, bool> predicate);
-
-
- /// <summary>
- /// Remove the item at a specific position of the list.
- /// </summary>
- /// <exception cref="IndexOutOfRangeException"> if <code>index</code> is negative or
- /// >= the size of the collection.</exception>
- /// <param name="index">The index of the item to remove.</param>
- /// <returns>The removed item.</returns>
- T RemoveAt(int index);
-
-
- /// <summary>
- /// Remove all items in an index interval.
- /// </summary>
- /// <exception cref="ArgumentOutOfRangeException"> if start or count
- /// is negative or start+count > the size of the collection.</exception>
- /// <param name="start">The index of the first item to remove.</param>
- /// <param name="count">The number of items to remove.</param>
- void RemoveInterval(int start, int count);
- }
-
- //TODO: decide if this should extend ICollection
- /// <summary>
- /// The interface describing the operations of a LIFO stack data structure.
- /// </summary>
- /// <typeparam name="T">The item type</typeparam>
- public interface IStack<T> : IDirectedCollectionValue<T>
- {
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- bool AllowsDuplicates { get;}
- /// <summary>
- /// Get the <code>index</code>'th element of the stack. The bottom of the stack has index 0.
- /// </summary>
- /// <param name="index"></param>
- /// <returns></returns>
- T this[int index] { get;}
- /// <summary>
- /// Push an item to the top of the stack.
- /// </summary>
- /// <param name="item">The item</param>
- void Push(T item);
- /// <summary>
- /// Pop the item at the top of the stack from the stack.
- /// </summary>
- /// <returns>The popped item.</returns>
- T Pop();
- }
-
- /// <summary>
- /// The interface describing the operations of a FIFO queue data structure.
- /// </summary>
- /// <typeparam name="T">The item type</typeparam>
- public interface IQueue<T> : IDirectedCollectionValue<T>
- {
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- bool AllowsDuplicates { get;}
- /// <summary>
- /// Get the <code>index</code>'th element of the queue. The front of the queue has index 0.
- /// </summary>
- /// <param name="index"></param>
- /// <returns></returns>
- T this[int index] { get;}
- /// <summary>
- /// Enqueue an item at the back of the queue.
- /// </summary>
- /// <param name="item">The item</param>
- void Enqueue(T item);
- /// <summary>
- /// Dequeue an item from the front of the queue.
- /// </summary>
- /// <returns>The item</returns>
- T Dequeue();
- }
-
-
- /// <summary>
- /// This is an indexed collection, where the item order is chosen by
- /// the user at insertion time.
- ///
- /// NBNBNB: we need a description of the view functionality here!
- /// </summary>
- public interface IList<T> : IIndexed<T>, IDisposable, SCG.IList<T>, System.Collections.IList
- {
- /// <summary>
- /// </summary>
- /// <exception cref="NoSuchItemException"> if this list is empty.</exception>
- /// <value>The first item in this list.</value>
- T First { get;}
-
- /// <summary>
- /// </summary>
- /// <exception cref="NoSuchItemException"> if this list is empty.</exception>
- /// <value>The last item in this list.</value>
- T Last { get;}
-
- /// <summary>
- /// Since <code>Add(T item)</code> always add at the end of the list,
- /// this describes if list has FIFO or LIFO semantics.
- /// </summary>
- /// <value>True if the <code>Remove()</code> operation removes from the
- /// start of the list, false if it removes from the end.</value>
- bool FIFO { get; set;}
-
- /// <summary>
- ///
- /// </summary>
- bool IsFixedSize { get; }
-
- /// <summary>
- /// On this list, this indexer is read/write.
- /// </summary>
- /// <exception cref="IndexOutOfRangeException"> if index is negative or
- /// >= the size of the collection.</exception>
- /// <value>The index'th item of this list.</value>
- /// <param name="index">The index of the item to fetch or store.</param>
- new T this[int index] { get; set;}
-
- #region Ambiguous calls when extending SCG.IList<T>
-
- #region SCG.ICollection<T>
- /// <summary>
- ///
- /// </summary>
- new int Count { get; }
-
- /// <summary>
- ///
- /// </summary>
- new bool IsReadOnly { get; }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <returns></returns>
- new bool Add(T item);
-
- /// <summary>
- ///
- /// </summary>
- new void Clear();
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <returns></returns>
- new bool Contains(T item);
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="array"></param>
- /// <param name="index"></param>
- new void CopyTo(T[] array, int index);
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <returns></returns>
- new bool Remove(T item);
-
- #endregion
-
- #region SCG.IList<T> proper
-
- /// <summary>
- /// Searches for an item in the list going forwards from the start.
- /// </summary>
- /// <param name="item">Item to search for.</param>
- /// <returns>Index of item from start. A negative number if item not found,
- /// namely the one's complement of the index at which the Add operation would put the item.</returns>
- new int IndexOf(T item);
-
- /// <summary>
- /// Remove the item at a specific position of the list.
- /// </summary>
- /// <exception cref="IndexOutOfRangeException"> if <code>index</code> is negative or
- /// >= the size of the collection.</exception>
- /// <param name="index">The index of the item to remove.</param>
- /// <returns>The removed item.</returns>
- new T RemoveAt(int index);
-
- #endregion
-
- #endregion
-
- /*/// <summary>
- /// Insert an item at a specific index location in this list.
- /// </summary>
- /// <exception cref="IndexOutOfRangeException"> if <code>index</code> is negative or
- /// > the size of the collection.</exception>
- /// <exception cref="DuplicateNotAllowedException"> if the list has
- /// <code>AllowsDuplicates==false</code> and the item is
- /// already in the list.</exception>
- /// <param name="index">The index at which to insert.</param>
- /// <param name="item">The item to insert.</param>
- void Insert(int index, T item);*/
-
- /// <summary>
- /// Insert an item at the end of a compatible view, used as a pointer.
- /// <para>The <code>pointer</code> must be a view on the same list as
- /// <code>this</code> and the endpoitn of <code>pointer</code> must be
- /// a valid insertion point of <code>this</code></para>
- /// </summary>
- /// <exception cref="IncompatibleViewException">If <code>pointer</code>
- /// is not a view on the same list as <code>this</code></exception>
- /// <exception cref="IndexOutOfRangeException"><b>??????</b> if the endpoint of
- /// <code>pointer</code> is not inside <code>this</code></exception>
- /// <exception cref="DuplicateNotAllowedException"> if the list has
- /// <code>AllowsDuplicates==false</code> and the item is
- /// already in the list.</exception>
- /// <param name="pointer"></param>
- /// <param name="item"></param>
- void Insert(IList<T> pointer, T item);
-
- /// <summary>
- /// Insert an item at the front of this list.
- /// <exception cref="DuplicateNotAllowedException"/> if the list has
- /// <code>AllowsDuplicates==false</code> and the item is
- /// already in the list.
- /// </summary>
- /// <param name="item">The item to insert.</param>
- void InsertFirst(T item);
-
- /// <summary>
- /// Insert an item at the back of this list.
- /// <exception cref="DuplicateNotAllowedException"/> if the list has
- /// <code>AllowsDuplicates==false</code> and the item is
- /// already in the list.
- /// </summary>
- /// <param name="item">The item to insert.</param>
- void InsertLast(T item);
-
- /// <summary>
- /// Insert into this list all items from an enumerable collection starting
- /// at a particular index.
- /// </summary>
- /// <exception cref="IndexOutOfRangeException"> if <code>index</code> is negative or
- /// > the size of the collection.</exception>
- /// <exception cref="DuplicateNotAllowedException"> if the list has
- /// <code>AllowsDuplicates==false</code> and one of the items to insert is
- /// already in the list.</exception>
- /// <param name="index">Index to start inserting at</param>
- /// <param name="items">Items to insert</param>
- /// <typeparam name="U"></typeparam>
- void InsertAll<U>(int index, SCG.IEnumerable<U> items) where U : T;
-
- /// <summary>
- /// Create a new list consisting of the items of this list satisfying a
- /// certain predicate.
- /// </summary>
- /// <param name="filter">The filter delegate defining the predicate.</param>
- /// <returns>The new list.</returns>
- IList<T> FindAll(Fun<T, bool> filter);
-
- /// <summary>
- /// Create a new list consisting of the results of mapping all items of this
- /// list. The new list will use the default equalityComparer for the item type V.
- /// </summary>
- /// <typeparam name="V">The type of items of the new list</typeparam>
- /// <param name="mapper">The delegate defining the map.</param>
- /// <returns>The new list.</returns>
- IList<V> Map<V>(Fun<T, V> mapper);
-
- /// <summary>
- /// Create a new list consisting of the results of mapping all items of this
- /// list. The new list will use a specified equalityComparer for the item type.
- /// </summary>
- /// <typeparam name="V">The type of items of the new list</typeparam>
- /// <param name="mapper">The delegate defining the map.</param>
- /// <param name="equalityComparer">The equalityComparer to use for the new list</param>
- /// <returns>The new list.</returns>
- IList<V> Map<V>(Fun<T, V> mapper, SCG.IEqualityComparer<V> equalityComparer);
-
- /// <summary>
- /// Remove one item from the list: from the front if <code>FIFO</code>
- /// is true, else from the back.
- /// <exception cref="NoSuchItemException"/> if this list is empty.
- /// </summary>
- /// <returns>The removed item.</returns>
- T Remove();
-
- /// <summary>
- /// Remove one item from the front of the list.
- /// <exception cref="NoSuchItemException"/> if this list is empty.
- /// </summary>
- /// <returns>The removed item.</returns>
- T RemoveFirst();
-
- /// <summary>
- /// Remove one item from the back of the list.
- /// <exception cref="NoSuchItemException"/> if this list is empty.
- /// </summary>
- /// <returns>The removed item.</returns>
- T RemoveLast();
-
- /// <summary>
- /// Create a list view on this list.
- /// <exception cref="ArgumentOutOfRangeException"/> if the view would not fit into
- /// this list.
- /// </summary>
- /// <param name="start">The index in this list of the start of the view.</param>
- /// <param name="count">The size of the view.</param>
- /// <returns>The new list view.</returns>
- IList<T> View(int start, int count);
-
- /// <summary>
- /// Create a list view on this list containing the (first) occurrence of a particular item.
- /// <exception cref="NoSuchItemException"/> if the item is not in this list.
- /// </summary>
- /// <param name="item">The item to find.</param>
- /// <returns>The new list view.</returns>
- IList<T> ViewOf(T item);
-
- /// <summary>
- /// Create a list view on this list containing the last occurrence of a particular item.
- /// <exception cref="NoSuchItemException"/> if the item is not in this list.
- /// </summary>
- /// <param name="item">The item to find.</param>
- /// <returns>The new list view.</returns>
- IList<T> LastViewOf(T item);
-
- /// <summary>
- /// Null if this list is not a view.
- /// </summary>
- /// <value>Underlying list for view.</value>
- IList<T> Underlying { get;}
-
- /// <summary>
- /// </summary>
- /// <value>Offset for this list view or 0 for an underlying list.</value>
- int Offset { get;}
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- bool IsValid { get;}
-
- /// <summary>
- /// Slide this list view along the underlying list.
- /// </summary>
- /// <exception cref="NotAViewException"> if this list is not a view.</exception>
- /// <exception cref="ArgumentOutOfRangeException"> if the operation
- /// would bring either end of the view outside the underlying list.</exception>
- /// <param name="offset">The signed amount to slide: positive to slide
- /// towards the end.</param>
- IList<T> Slide(int offset);
-
- /// <summary>
- /// Slide this list view along the underlying list, changing its size.
- ///
- /// </summary>
- /// <exception cref="NotAViewException"> if this list is not a view.</exception>
- /// <exception cref="ArgumentOutOfRangeException"> if the operation
- /// would bring either end of the view outside the underlying list.</exception>
- /// <param name="offset">The signed amount to slide: positive to slide
- /// towards the end.</param>
- /// <param name="size">The new size of the view.</param>
- IList<T> Slide(int offset, int size);
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="offset"></param>
- /// <returns></returns>
- bool TrySlide(int offset);
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="offset"></param>
- /// <param name="size"></param>
- /// <returns></returns>
- bool TrySlide(int offset, int size);
-
- /// <summary>
- ///
- /// <para>Returns null if <code>otherView</code> is strictly to the left of this view</para>
- /// </summary>
- /// <param name="otherView"></param>
- /// <exception cref="IncompatibleViewException">If otherView does not have the same underlying list as this</exception>
- /// <exception cref="ArgumentOutOfRangeException">If <code>otherView</code> is strictly to the left of this view</exception>
- /// <returns></returns>
- IList<T> Span(IList<T> otherView);
-
- /// <summary>
- /// Reverse the list so the items are in the opposite sequence order.
- /// </summary>
- void Reverse();
-
- /// <summary>
- /// Check if this list is sorted according to the default sorting order
- /// for the item type T, as defined by the <see cref="T:C5.Comparer`1"/> class
- /// </summary>
- /// <exception cref="NotComparableException">if T is not comparable</exception>
- /// <returns>True if the list is sorted, else false.</returns>
- bool IsSorted();
-
- /// <summary>
- /// Check if this list is sorted according to a specific sorting order.
- /// </summary>
- /// <param name="comparer">The comparer defining the sorting order.</param>
- /// <returns>True if the list is sorted, else false.</returns>
- bool IsSorted(SCG.IComparer<T> comparer);
-
- /// <summary>
- /// Sort the items of the list according to the default sorting order
- /// for the item type T, as defined by the <see cref="T:C5.Comparer`1"/> class
- /// </summary>
- /// <exception cref="NotComparableException">if T is not comparable</exception>
- void Sort();
-
- /// <summary>
- /// Sort the items of the list according to a specified sorting order.
- /// <para>The sorting does not perform duplicate elimination or identify items
- /// according to the comparer or itemequalityComparer. I.e. the list as an
- /// unsequenced collection with binary equality, will not change.
- /// </para>
- /// </summary>
- /// <param name="comparer">The comparer defining the sorting order.</param>
- void Sort(SCG.IComparer<T> comparer);
-
-
- /// <summary>
- /// Randomly shuffle the items of this list.
- /// </summary>
- void Shuffle();
-
-
- /// <summary>
- /// Shuffle the items of this list according to a specific random source.
- /// </summary>
- /// <param name="rnd">The random source.</param>
- void Shuffle(Random rnd);
- }
-
-
- /// <summary>
- /// The base type of a priority queue handle
- /// </summary>
- /// <typeparam name="T"></typeparam>
- public interface IPriorityQueueHandle<T>
- {
- //TODO: make abstract and prepare for double dispatch:
- //public virtual bool Delete(IPriorityQueue<T> q) { throw new InvalidFooException();}
- //bool Replace(T item);
- }
-
-
- /// <summary>
- /// A generic collection of items prioritized by a comparison (order) relation.
- /// Supports adding items and reporting or removing extremal elements.
- /// <para>
- ///
- /// </para>
- /// When adding an item, the user may choose to have a handle allocated for this item in the queue.
- /// The resulting handle may be used for deleting the item even if not extremal, and for replacing the item.
- /// A priority queue typically only holds numeric priorities associated with some objects
- /// maintained separately in other collection objects.
- /// </summary>
- public interface IPriorityQueue<T> : IExtensible<T>
- {
- /// <summary>
- /// Find the current least item of this priority queue.
- /// </summary>
- /// <returns>The least item.</returns>
- T FindMin();
-
-
- /// <summary>
- /// Remove the least item from this priority queue.
- /// </summary>
- /// <returns>The removed item.</returns>
- T DeleteMin();
-
-
- /// <summary>
- /// Find the current largest item of this priority queue.
- /// </summary>
- /// <returns>The largest item.</returns>
- T FindMax();
-
-
- /// <summary>
- /// Remove the largest item from this priority queue.
- /// </summary>
- /// <returns>The removed item.</returns>
- T DeleteMax();
-
- /// <summary>
- /// The comparer object supplied at creation time for this collection
- /// </summary>
- /// <value>The comparer</value>
- SCG.IComparer<T> Comparer { get;}
- /// <summary>
- /// Get or set the item corresponding to a handle. Throws exceptions on
- /// invalid handles.
- /// </summary>
- /// <param name="handle"></param>
- /// <returns></returns>
- T this[IPriorityQueueHandle<T> handle] { get; set;}
-
- /// <summary>
- /// Check if the entry corresponding to a handle is in the priority queue.
- /// </summary>
- /// <param name="handle"></param>
- /// <param name="item"></param>
- /// <returns></returns>
- bool Find(IPriorityQueueHandle<T> handle, out T item);
-
- /// <summary>
- /// Add an item to the priority queue, receiving a
- /// handle for the item in the queue,
- /// or reusing an existing unused handle.
- /// </summary>
- /// <param name="handle">On output: a handle for the added item.
- /// On input: null for allocating a new handle, or a currently unused handle for reuse.
- /// A handle for reuse must be compatible with this priority queue,
- /// by being created by a priority queue of the same runtime type, but not
- /// necessarily the same priority queue object.</param>
- /// <param name="item"></param>
- /// <returns></returns>
- bool Add(ref IPriorityQueueHandle<T> handle, T item);
-
- /// <summary>
- /// Delete an item with a handle from a priority queue
- /// </summary>
- /// <param name="handle">The handle for the item. The handle will be invalidated, but reusable.</param>
- /// <returns>The deleted item</returns>
- T Delete(IPriorityQueueHandle<T> handle);
-
- /// <summary>
- /// Replace an item with a handle in a priority queue with a new item.
- /// Typically used for changing the priority of some queued object.
- /// </summary>
- /// <param name="handle">The handle for the old item</param>
- /// <param name="item">The new item</param>
- /// <returns>The old item</returns>
- T Replace(IPriorityQueueHandle<T> handle, T item);
-
- /// <summary>
- /// Find the current least item of this priority queue.
- /// </summary>
- /// <param name="handle">On return: the handle of the item.</param>
- /// <returns>The least item.</returns>
- T FindMin(out IPriorityQueueHandle<T> handle);
-
- /// <summary>
- /// Find the current largest item of this priority queue.
- /// </summary>
- /// <param name="handle">On return: the handle of the item.</param>
- /// <returns>The largest item.</returns>
-
- T FindMax(out IPriorityQueueHandle<T> handle);
-
- /// <summary>
- /// Remove the least item from this priority queue.
- /// </summary>
- /// <param name="handle">On return: the handle of the removed item.</param>
- /// <returns>The removed item.</returns>
-
- T DeleteMin(out IPriorityQueueHandle<T> handle);
-
- /// <summary>
- /// Remove the largest item from this priority queue.
- /// </summary>
- /// <param name="handle">On return: the handle of the removed item.</param>
- /// <returns>The removed item.</returns>
- T DeleteMax(out IPriorityQueueHandle<T> handle);
- }
-
-
-
- /// <summary>
- /// A sorted collection, i.e. a collection where items are maintained and can be searched for in sorted order.
- /// Thus the sequence order is given as a sorting order.
- ///
- /// <para>The sorting order is defined by a comparer, an object of type IComparer<T>
- /// (<see cref="T:C5.IComparer`1"/>). Implementors of this interface will normally let the user
- /// define the comparer as an argument to a constructor.
- /// Usually there will also be constructors without a comparer argument, in which case the
- /// comparer should be the defalt comparer for the item type, <see cref="P:C5.Comparer`1.Default"/>.</para>
- ///
- /// <para>The comparer of the sorted collection is available as the <code>Comparer</code> property
- /// (<see cref="P:C5.ISorted`1.Comparer"/>).</para>
- ///
- /// <para>The methods are grouped according to
- /// <list>
- /// <item>Extrema: report or report and delete an extremal item. This is reminiscent of simplified priority queues.</item>
- /// <item>Nearest neighbor: report predecessor or successor in the collection of an item. Cut belongs to this group.</item>
- /// <item>Range: report a view of a range of elements or remove all elements in a range.</item>
- /// <item>AddSorted: add a collection of items known to be sorted in the same order (should be faster) (to be removed?)</item>
- /// </list>
- /// </para>
- ///
- /// <para>Since this interface extends ISequenced<T>, sorted collections will also have an
- /// item equalityComparer (<see cref="P:C5.IExtensible`1.EqualityComparer"/>). This equalityComparer will not be used in connection with
- /// the inner workings of the sorted collection, but will be used if the sorted collection is used as
- /// an item in a collection of unsequenced or sequenced collections,
- /// (<see cref="T:C5.ICollection`1"/> and <see cref="T:C5.ISequenced`1"/>)</para>
- ///
- /// <para>Note that code may check if two sorted collections has the same sorting order
- /// by checking if the Comparer properties are equal. This is done a few places in this library
- /// for optimization purposes.</para>
- /// </summary>
- public interface ISorted<T> : ISequenced<T>
- {
- /// <summary>
- /// Find the current least item of this sorted collection.
- /// </summary>
- /// <exception cref="NoSuchItemException"> if the collection is empty.</exception>
- /// <returns>The least item.</returns>
- T FindMin();
-
-
- /// <summary>
- /// Remove the least item from this sorted collection.
- /// </summary>
- /// <exception cref="NoSuchItemException"> if the collection is empty.</exception>
- /// <returns>The removed item.</returns>
- T DeleteMin();
-
-
- /// <summary>
- /// Find the current largest item of this sorted collection.
- /// </summary>
- /// <exception cref="NoSuchItemException"> if the collection is empty.</exception>
- /// <returns>The largest item.</returns>
- T FindMax();
-
-
- /// <summary>
- /// Remove the largest item from this sorted collection.
- /// </summary>
- /// <exception cref="NoSuchItemException"> if the collection is empty.</exception>
- /// <returns>The removed item.</returns>
- T DeleteMax();
-
- /// <summary>
- /// The comparer object supplied at creation time for this sorted collection.
- /// </summary>
- /// <value>The comparer</value>
- SCG.IComparer<T> Comparer { get; }
-
- /// <summary>
- /// Find the strict predecessor of item in the sorted collection,
- /// that is, the greatest item in the collection smaller than the item.
- /// </summary>
- /// <param name="item">The item to find the predecessor for.</param>
- /// <param name="res">The predecessor, if any; otherwise the default value for T.</param>
- /// <returns>True if item has a predecessor; otherwise false.</returns>
- bool TryPredecessor(T item, out T res);
-
-
- /// <summary>
- /// Find the strict successor of item in the sorted collection,
- /// that is, the least item in the collection greater than the supplied value.
- /// </summary>
- /// <param name="item">The item to find the successor for.</param>
- /// <param name="res">The successor, if any; otherwise the default value for T.</param>
- /// <returns>True if item has a successor; otherwise false.</returns>
- bool TrySuccessor(T item, out T res);
-
-
- /// <summary>
- /// Find the weak predecessor of item in the sorted collection,
- /// that is, the greatest item in the collection smaller than or equal to the item.
- /// </summary>
- /// <param name="item">The item to find the weak predecessor for.</param>
- /// <param name="res">The weak predecessor, if any; otherwise the default value for T.</param>
- /// <returns>True if item has a weak predecessor; otherwise false.</returns>
- bool TryWeakPredecessor(T item, out T res);
-
-
- /// <summary>
- /// Find the weak successor of item in the sorted collection,
- /// that is, the least item in the collection greater than or equal to the supplied value.
- /// </summary>
- /// <param name="item">The item to find the weak successor for.</param>
- /// <param name="res">The weak successor, if any; otherwise the default value for T.</param>
- /// <returns>True if item has a weak successor; otherwise false.</returns>
- bool TryWeakSuccessor(T item, out T res);
-
-
- /// <summary>
- /// Find the strict predecessor in the sorted collection of a particular value,
- /// that is, the largest item in the collection less than the supplied value.
- /// </summary>
- /// <exception cref="NoSuchItemException"> if no such element exists (the
- /// supplied value is less than or equal to the minimum of this collection.)</exception>
- /// <param name="item">The item to find the predecessor for.</param>
- /// <returns>The predecessor.</returns>
- T Predecessor(T item);
-
-
- /// <summary>
- /// Find the strict successor in the sorted collection of a particular value,
- /// that is, the least item in the collection greater than the supplied value.
- /// </summary>
- /// <exception cref="NoSuchItemException"> if no such element exists (the
- /// supplied value is greater than or equal to the maximum of this collection.)</exception>
- /// <param name="item">The item to find the successor for.</param>
- /// <returns>The successor.</returns>
- T Successor(T item);
-
-
- /// <summary>
- /// Find the weak predecessor in the sorted collection of a particular value,
- /// that is, the largest item in the collection less than or equal to the supplied value.
- /// </summary>
- /// <exception cref="NoSuchItemException"> if no such element exists (the
- /// supplied value is less than the minimum of this collection.)</exception>
- /// <param name="item">The item to find the weak predecessor for.</param>
- /// <returns>The weak predecessor.</returns>
- T WeakPredecessor(T item);
-
-
- /// <summary>
- /// Find the weak successor in the sorted collection of a particular value,
- /// that is, the least item in the collection greater than or equal to the supplied value.
- /// </summary>
- /// <exception cref="NoSuchItemException"> if no such element exists (the
- /// supplied value is greater than the maximum of this collection.)</exception>
- ///<param name="item">The item to find the weak successor for.</param>
- /// <returns>The weak successor.</returns>
- T WeakSuccessor(T item);
-
-
- /// <summary>
- /// Given a "cut" function from the items of the sorted collection to <code>int</code>
- /// whose only sign changes when going through items in increasing order
- /// can be
- /// <list>
- /// <item>from positive to zero</item>
- /// <item>from positive to negative</item>
- /// <item>from zero to negative</item>
- /// </list>
- /// The "cut" function is supplied as the <code>CompareTo</code> method
- /// of an object <code>c</code> implementing
- /// <code>IComparable<T></code>.
- /// A typical example is the case where <code>T</code> is comparable and
- /// <code>cutFunction</code> is itself of type <code>T</code>.
- /// <para>This method performs a search in the sorted collection for the ranges in which the
- /// "cut" function is negative, zero respectively positive. If <code>T</code> is comparable
- /// and <code>c</code> is of type <code>T</code>, this is a safe way (no exceptions thrown)
- /// to find predecessor and successor of <code>c</code>.
- /// </para>
- /// <para> If the supplied cut function does not satisfy the sign-change condition,
- /// the result of this call is undefined.
- /// </para>
- ///
- /// </summary>
- /// <param name="cutFunction">The cut function <code>T</code> to <code>int</code>, given
- /// by the <code>CompareTo</code> method of an object implementing
- /// <code>IComparable<T></code>.</param>
- /// <param name="low">Returns the largest item in the collection, where the
- /// cut function is positive (if any).</param>
- /// <param name="lowIsValid">Returns true if the cut function is positive somewhere
- /// on this collection.</param>
- /// <param name="high">Returns the least item in the collection, where the
- /// cut function is negative (if any).</param>
- /// <param name="highIsValid">Returns true if the cut function is negative somewhere
- /// on this collection.</param>
- /// <returns>True if the cut function is zero somewhere
- /// on this collection.</returns>
- bool Cut(IComparable<T> cutFunction, out T low, out bool lowIsValid, out T high, out bool highIsValid);
-
-
- /// <summary>
- /// Query this sorted collection for items greater than or equal to a supplied value.
- /// <para>The returned collection is not a copy but a view into the collection.</para>
- /// <para>The view is fragile in the sense that changes to the underlying collection will
- /// invalidate the view so that further operations on the view throws InvalidView exceptions.</para>
- /// </summary>
- /// <param name="bot">The lower bound (inclusive).</param>
- /// <returns>The result directed collection.</returns>
- IDirectedEnumerable<T> RangeFrom(T bot);
-
-
- /// <summary>
- /// Query this sorted collection for items between two supplied values.
- /// <para>The returned collection is not a copy but a view into the collection.</para>
- /// <para>The view is fragile in the sense that changes to the underlying collection will
- /// invalidate the view so that further operations on the view throws InvalidView exceptions.</para>
- /// </summary>
- /// <param name="bot">The lower bound (inclusive).</param>
- /// <param name="top">The upper bound (exclusive).</param>
- /// <returns>The result directed collection.</returns>
- IDirectedEnumerable<T> RangeFromTo(T bot, T top);
-
-
- /// <summary>
- /// Query this sorted collection for items less than a supplied value.
- /// <para>The returned collection is not a copy but a view into the collection.</para>
- /// <para>The view is fragile in the sense that changes to the underlying collection will
- /// invalidate the view so that further operations on the view throws InvalidView exceptions.</para>
- /// </summary>
- /// <param name="top">The upper bound (exclusive).</param>
- /// <returns>The result directed collection.</returns>
- IDirectedEnumerable<T> RangeTo(T top);
-
-
- /// <summary>
- /// Create a directed collection with the same items as this collection.
- /// <para>The returned collection is not a copy but a view into the collection.</para>
- /// <para>The view is fragile in the sense that changes to the underlying collection will
- /// invalidate the view so that further operations on the view throws InvalidView exceptions.</para>
- /// </summary>
- /// <returns>The result directed collection.</returns>
- IDirectedCollectionValue<T> RangeAll();
-
-
- //TODO: remove now that we assume that we can check the sorting order?
- /// <summary>
- /// Add all the items from another collection with an enumeration order that
- /// is increasing in the items.
- /// </summary>
- /// <exception cref="ArgumentException"> if the enumerated items turns out
- /// not to be in increasing order.</exception>
- /// <param name="items">The collection to add.</param>
- /// <typeparam name="U"></typeparam>
- void AddSorted<U>(SCG.IEnumerable<U> items) where U : T;
-
-
- /// <summary>
- /// Remove all items of this collection above or at a supplied threshold.
- /// </summary>
- /// <param name="low">The lower threshold (inclusive).</param>
- void RemoveRangeFrom(T low);
-
-
- /// <summary>
- /// Remove all items of this collection between two supplied thresholds.
- /// </summary>
- /// <param name="low">The lower threshold (inclusive).</param>
- /// <param name="hi">The upper threshold (exclusive).</param>
- void RemoveRangeFromTo(T low, T hi);
-
-
- /// <summary>
- /// Remove all items of this collection below a supplied threshold.
- /// </summary>
- /// <param name="hi">The upper threshold (exclusive).</param>
- void RemoveRangeTo(T hi);
- }
-
-
-
- /// <summary>
- /// A collection where items are maintained in sorted order together
- /// with their indexes in that order.
- /// </summary>
- public interface IIndexedSorted<T> : ISorted<T>, IIndexed<T>
- {
- /// <summary>
- /// Determine the number of items at or above a supplied threshold.
- /// </summary>
- /// <param name="bot">The lower bound (inclusive)</param>
- /// <returns>The number of matcing items.</returns>
- int CountFrom(T bot);
-
-
- /// <summary>
- /// Determine the number of items between two supplied thresholds.
- /// </summary>
- /// <param name="bot">The lower bound (inclusive)</param>
- /// <param name="top">The upper bound (exclusive)</param>
- /// <returns>The number of matcing items.</returns>
- int CountFromTo(T bot, T top);
-
-
- /// <summary>
- /// Determine the number of items below a supplied threshold.
- /// </summary>
- /// <param name="top">The upper bound (exclusive)</param>
- /// <returns>The number of matcing items.</returns>
- int CountTo(T top);
-
-
- /// <summary>
- /// Query this sorted collection for items greater than or equal to a supplied value.
- /// </summary>
- /// <param name="bot">The lower bound (inclusive).</param>
- /// <returns>The result directed collection.</returns>
- new IDirectedCollectionValue<T> RangeFrom(T bot);
-
-
- /// <summary>
- /// Query this sorted collection for items between two supplied values.
- /// </summary>
- /// <param name="bot">The lower bound (inclusive).</param>
- /// <param name="top">The upper bound (exclusive).</param>
- /// <returns>The result directed collection.</returns>
- new IDirectedCollectionValue<T> RangeFromTo(T bot, T top);
-
-
- /// <summary>
- /// Query this sorted collection for items less than a supplied value.
- /// </summary>
- /// <param name="top">The upper bound (exclusive).</param>
- /// <returns>The result directed collection.</returns>
- new IDirectedCollectionValue<T> RangeTo(T top);
-
-
- /// <summary>
- /// Create a new indexed sorted collection consisting of the items of this
- /// indexed sorted collection satisfying a certain predicate.
- /// </summary>
- /// <param name="predicate">The filter delegate defining the predicate.</param>
- /// <returns>The new indexed sorted collection.</returns>
- IIndexedSorted<T> FindAll(Fun<T, bool> predicate);
-
-
- /// <summary>
- /// Create a new indexed sorted collection consisting of the results of
- /// mapping all items of this list.
- /// <exception cref="ArgumentException"/> if the map is not increasing over
- /// the items of this collection (with respect to the two given comparison
- /// relations).
- /// </summary>
- /// <param name="mapper">The delegate definging the map.</param>
- /// <param name="comparer">The comparion relation to use for the result.</param>
- /// <returns>The new sorted collection.</returns>
- IIndexedSorted<V> Map<V>(Fun<T, V> mapper, SCG.IComparer<V> comparer);
- }
-
-
-
- /// <summary>
- /// The type of a sorted collection with persistence
- /// </summary>
- public interface IPersistentSorted<T> : ISorted<T>, IDisposable
- {
- /// <summary>
- /// Make a (read-only) snap shot of this collection.
- /// </summary>
- /// <returns>The snap shot.</returns>
- ISorted<T> Snapshot();
- }
-
-
-
- /*************************************************************************/
- /// <summary>
- /// A dictionary with keys of type K and values of type V. Equivalent to a
- /// finite partial map from K to V.
- /// </summary>
- public interface IDictionary<K, V> : ICollectionValue<KeyValuePair<K, V>>, ICloneable
- {
- /// <summary>
- /// The key equalityComparer.
- /// </summary>
- /// <value></value>
- SCG.IEqualityComparer<K> EqualityComparer { get;}
-
- /// <summary>
- /// Indexer for dictionary.
- /// </summary>
- /// <exception cref="NoSuchItemException"> if no entry is found. </exception>
- /// <value>The value corresponding to the key</value>
- V this[K key] { get; set;}
-
-
- /// <summary>
- ///
- /// </summary>
- /// <value>True if dictionary is read-only</value>
- bool IsReadOnly { get;}
-
-
- /// <summary>
- ///
- /// </summary>
- /// <value>A collection containg the all the keys of the dictionary</value>
- ICollectionValue<K> Keys { get;}
-
-
- /// <summary>
- ///
- /// </summary>
- /// <value>A collection containing all the values of the dictionary</value>
- ICollectionValue<V> Values { get;}
-
- /// <summary>
- ///
- /// </summary>
- /// <value>A delegate of type <see cref="T:C5.Fun`2"/> defining the partial function from K to V give by the dictionary.</value>
- Fun<K, V> Fun { get; }
-
-
- //TODO: resolve inconsistency: Add thows exception if key already there, AddAll ignores keys already There?
- /// <summary>
- /// Add a new (key, value) pair (a mapping) to the dictionary.
- /// </summary>
- /// <exception cref="DuplicateNotAllowedException"> if there already is an entry with the same key. </exception>>
- /// <param name="key">Key to add</param>
- /// <param name="val">Value to add</param>
- void Add(K key, V val);
-
- /// <summary>
- /// Add the entries from a collection of <see cref="T:C5.KeyValuePair`2"/> pairs to this dictionary.
- /// </summary>
- /// <exception cref="DuplicateNotAllowedException">
- /// If the input contains duplicate keys or a key already present in this dictionary.</exception>
- /// <param name="entries"></param>
- void AddAll<U, W>(SCG.IEnumerable<KeyValuePair<U, W>> entries)
- where U : K
- where W : V
- ;
-
- /// <summary>
- /// The value is symbolic indicating the type of asymptotic complexity
- /// in terms of the size of this collection (worst-case or amortized as
- /// relevant).
- /// <para>See <see cref="T:C5.Speed"/> for the set of symbols.</para>
- /// </summary>
- /// <value>A characterization of the speed of lookup operations
- /// (<code>Contains()</code> etc.) of the implementation of this dictionary.</value>
- Speed ContainsSpeed { get;}
-
- /// <summary>
- /// Check whether this collection contains all the values in another collection.
- /// If this collection has bag semantics (<code>AllowsDuplicates==true</code>)
- /// the check is made with respect to multiplicities, else multiplicities
- /// are not taken into account.
- /// </summary>
- /// <param name="items">The </param>
- /// <returns>True if all values in <code>items</code>is in this collection.</returns>
- bool ContainsAll<H>(SCG.IEnumerable<H> items) where H : K;
-
- /// <summary>
- /// Remove an entry with a given key from the dictionary
- /// </summary>
- /// <param name="key">The key of the entry to remove</param>
- /// <returns>True if an entry was found (and removed)</returns>
- bool Remove(K key);
-
-
- /// <summary>
- /// Remove an entry with a given key from the dictionary and report its value.
- /// </summary>
- /// <param name="key">The key of the entry to remove</param>
- /// <param name="val">On exit, the value of the removed entry</param>
- /// <returns>True if an entry was found (and removed)</returns>
- bool Remove(K key, out V val);
-
-
- /// <summary>
- /// Remove all entries from the dictionary
- /// </summary>
- void Clear();
-
-
- /// <summary>
- /// Check if there is an entry with a specified key
- /// </summary>
- /// <param name="key">The key to look for</param>
- /// <returns>True if key was found</returns>
- bool Contains(K key);
-
-
- /// <summary>
- /// Check if there is an entry with a specified key and report the corresponding
- /// value if found. This can be seen as a safe form of "val = this[key]".
- /// </summary>
- /// <param name="key">The key to look for</param>
- /// <param name="val">On exit, the value of the entry</param>
- /// <returns>True if key was found</returns>
- bool Find(K key, out V val);
-
- /// <summary>
- /// Check if there is an entry with a specified key and report the corresponding
- /// value if found. This can be seen as a safe form of "val = this[key]".
- /// </summary>
- /// <param name="key">The key to look for</param>
- /// <param name="val">On exit, the value of the entry</param>
- /// <returns>True if key was found</returns>
- bool Find(ref K key, out V val);
-
-
- /// <summary>
- /// Look for a specific key in the dictionary and if found replace the value with a new one.
- /// This can be seen as a non-adding version of "this[key] = val".
- /// </summary>
- /// <param name="key">The key to look for</param>
- /// <param name="val">The new value</param>
- /// <returns>True if key was found</returns>
- bool Update(K key, V val); //no-adding
-
-
- /// <summary>
- /// Look for a specific key in the dictionary and if found replace the value with a new one.
- /// This can be seen as a non-adding version of "this[key] = val" reporting the old value.
- /// </summary>
- /// <param name="key">The key to look for</param>
- /// <param name="val">The new value</param>
- /// <param name="oldval">The old value if any</param>
- /// <returns>True if key was found</returns>
- bool Update(K key, V val, out V oldval); //no-adding
-
- /// <summary>
- /// Look for a specific key in the dictionary. If found, report the corresponding value,
- /// else add an entry with the key and the supplied value.
- /// </summary>
- /// <param name="key">The key to look for</param>
- /// <param name="val">On entry the value to add if the key is not found.
- /// On exit the value found if any.</param>
- /// <returns>True if key was found</returns>
- bool FindOrAdd(K key, ref V val); //mixture
-
-
- /// <summary>
- /// Update value in dictionary corresponding to key if found, else add new entry.
- /// More general than "this[key] = val;" by reporting if key was found.
- /// </summary>
- /// <param name="key">The key to look for</param>
- /// <param name="val">The value to add or replace with.</param>
- /// <returns>True if key was found and value updated.</returns>
- bool UpdateOrAdd(K key, V val);
-
-
- /// <summary>
- /// Update value in dictionary corresponding to key if found, else add new entry.
- /// More general than "this[key] = val;" by reporting if key was found.
- /// </summary>
- /// <param name="key">The key to look for</param>
- /// <param name="val">The value to add or replace with.</param>
- /// <param name="oldval">The old value if any</param>
- /// <returns>True if key was found and value updated.</returns>
- bool UpdateOrAdd(K key, V val, out V oldval);
-
-
- /// <summary>
- /// Check the integrity of the internal data structures of this dictionary.
- /// Only avaliable in DEBUG builds???
- /// </summary>
- /// <returns>True if check does not fail.</returns>
- bool Check();
- }
-
-
-
- /// <summary>
- /// A dictionary with sorted keys.
- /// </summary>
- public interface ISortedDictionary<K, V> : IDictionary<K, V>
- {
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- new ISorted<K> Keys { get;}
-
- /// <summary>
- /// Find the current least item of this sorted collection.
- /// </summary>
- /// <exception cref="NoSuchItemException"> if the collection is empty.</exception>
- /// <returns>The least item.</returns>
- KeyValuePair<K, V> FindMin();
-
-
- /// <summary>
- /// Remove the least item from this sorted collection.
- /// </summary>
- /// <exception cref="NoSuchItemException"> if the collection is empty.</exception>
- /// <returns>The removed item.</returns>
- KeyValuePair<K, V> DeleteMin();
-
-
- /// <summary>
- /// Find the current largest item of this sorted collection.
- /// </summary>
- /// <exception cref="NoSuchItemException"> if the collection is empty.</exception>
- /// <returns>The largest item.</returns>
- KeyValuePair<K, V> FindMax();
-
-
- /// <summary>
- /// Remove the largest item from this sorted collection.
- /// </summary>
- /// <exception cref="NoSuchItemException"> if the collection is empty.</exception>
- /// <returns>The removed item.</returns>
- KeyValuePair<K, V> DeleteMax();
-
- /// <summary>
- /// The key comparer used by this dictionary.
- /// </summary>
- /// <value></value>
- SCG.IComparer<K> Comparer { get;}
-
- /// <summary>
- /// Find the entry in the dictionary whose key is the
- /// predecessor of the specified key.
- /// </summary>
- /// <param name="key">The key</param>
- /// <param name="res">The predecessor, if any</param>
- /// <returns>True if key has a predecessor</returns>
- bool TryPredecessor(K key, out KeyValuePair<K, V> res);
-
- /// <summary>
- /// Find the entry in the dictionary whose key is the
- /// successor of the specified key.
- /// </summary>
- /// <param name="key">The key</param>
- /// <param name="res">The successor, if any</param>
- /// <returns>True if the key has a successor</returns>
- bool TrySuccessor(K key, out KeyValuePair<K, V> res);
-
- /// <summary>
- /// Find the entry in the dictionary whose key is the
- /// weak predecessor of the specified key.
- /// </summary>
- /// <param name="key">The key</param>
- /// <param name="res">The predecessor, if any</param>
- /// <returns>True if key has a weak predecessor</returns>
- bool TryWeakPredecessor(K key, out KeyValuePair<K, V> res);
-
- /// <summary>
- /// Find the entry in the dictionary whose key is the
- /// weak successor of the specified key.
- /// </summary>
- /// <param name="key">The key</param>
- /// <param name="res">The weak successor, if any</param>
- /// <returns>True if the key has a weak successor</returns>
- bool TryWeakSuccessor(K key, out KeyValuePair<K, V> res);
-
- /// <summary>
- /// Find the entry with the largest key less than a given key.
- /// </summary>
- /// <exception cref="NoSuchItemException"> if there is no such entry. </exception>
- /// <param name="key">The key to compare to</param>
- /// <returns>The entry</returns>
- KeyValuePair<K, V> Predecessor(K key);
-
-
- /// <summary>
- /// Find the entry with the least key greater than a given key.
- /// </summary>
- /// <exception cref="NoSuchItemException"> if there is no such entry. </exception>
- /// <param name="key">The key to compare to</param>
- /// <returns>The entry</returns>
- KeyValuePair<K, V> Successor(K key);
-
-
- /// <summary>
- /// Find the entry with the largest key less than or equal to a given key.
- /// </summary>
- /// <exception cref="NoSuchItemException"> if there is no such entry. </exception>
- /// <param name="key">The key to compare to</param>
- /// <returns>The entry</returns>
- KeyValuePair<K, V> WeakPredecessor(K key);
-
-
- /// <summary>
- /// Find the entry with the least key greater than or equal to a given key.
- /// </summary>
- /// <exception cref="NoSuchItemException"> if there is no such entry. </exception>
- /// <param name="key">The key to compare to</param>
- /// <returns>The entry</returns>
- KeyValuePair<K, V> WeakSuccessor(K key);
-
- /// <summary>
- /// Given a "cut" function from the items of the sorted collection to <code>int</code>
- /// whose only sign changes when going through items in increasing order
- /// can be
- /// <list>
- /// <item>from positive to zero</item>
- /// <item>from positive to negative</item>
- /// <item>from zero to negative</item>
- /// </list>
- /// The "cut" function is supplied as the <code>CompareTo</code> method
- /// of an object <code>c</code> implementing
- /// <code>IComparable<K></code>.
- /// A typical example is the case where <code>K</code> is comparable and
- /// <code>c</code> is itself of type <code>K</code>.
- /// <para>This method performs a search in the sorted collection for the ranges in which the
- /// "cut" function is negative, zero respectively positive. If <code>K</code> is comparable
- /// and <code>c</code> is of type <code>K</code>, this is a safe way (no exceptions thrown)
- /// to find predecessor and successor of <code>c</code>.
- /// </para>
- /// <para> If the supplied cut function does not satisfy the sign-change condition,
- /// the result of this call is undefined.
- /// </para>
- ///
- /// </summary>
- /// <param name="cutFunction">The cut function <code>K</code> to <code>int</code>, given
- /// by the <code>CompareTo</code> method of an object implementing
- /// <code>IComparable<K></code>.</param>
- /// <param name="lowEntry">Returns the largest item in the collection, where the
- /// cut function is positive (if any).</param>
- /// <param name="lowIsValid">Returns true if the cut function is positive somewhere
- /// on this collection.</param>
- /// <param name="highEntry">Returns the least item in the collection, where the
- /// cut function is negative (if any).</param>
- /// <param name="highIsValid">Returns true if the cut function is negative somewhere
- /// on this collection.</param>
- /// <returns>True if the cut function is zero somewhere
- /// on this collection.</returns>
- bool Cut(IComparable<K> cutFunction, out KeyValuePair<K, V> lowEntry, out bool lowIsValid, out KeyValuePair<K, V> highEntry, out bool highIsValid);
-
- /// <summary>
- /// Query this sorted collection for items greater than or equal to a supplied value.
- /// <para>The returned collection is not a copy but a view into the collection.</para>
- /// <para>The view is fragile in the sense that changes to the underlying collection will
- /// invalidate the view so that further operations on the view throws InvalidView exceptions.</para>
- /// </summary>
- /// <param name="bot">The lower bound (inclusive).</param>
- /// <returns>The result directed collection.</returns>
- IDirectedEnumerable<KeyValuePair<K, V>> RangeFrom(K bot);
-
-
- /// <summary>
- /// Query this sorted collection for items between two supplied values.
- /// <para>The returned collection is not a copy but a view into the collection.</para>
- /// <para>The view is fragile in the sense that changes to the underlying collection will
- /// invalidate the view so that further operations on the view throws InvalidView exceptions.</para>
- /// </summary>
- /// <param name="lowerBound">The lower bound (inclusive).</param>
- /// <param name="upperBound">The upper bound (exclusive).</param>
- /// <returns>The result directed collection.</returns>
- IDirectedEnumerable<KeyValuePair<K, V>> RangeFromTo(K lowerBound, K upperBound);
-
-
- /// <summary>
- /// Query this sorted collection for items less than a supplied value.
- /// <para>The returned collection is not a copy but a view into the collection.</para>
- /// <para>The view is fragile in the sense that changes to the underlying collection will
- /// invalidate the view so that further operations on the view throws InvalidView exceptions.</para>
- /// </summary>
- /// <param name="top">The upper bound (exclusive).</param>
- /// <returns>The result directed collection.</returns>
- IDirectedEnumerable<KeyValuePair<K, V>> RangeTo(K top);
-
-
- /// <summary>
- /// Create a directed collection with the same items as this collection.
- /// <para>The returned collection is not a copy but a view into the collection.</para>
- /// <para>The view is fragile in the sense that changes to the underlying collection will
- /// invalidate the view so that further operations on the view throws InvalidView exceptions.</para>
- /// </summary>
- /// <returns>The result directed collection.</returns>
- IDirectedCollectionValue<KeyValuePair<K, V>> RangeAll();
-
-
- //TODO: remove now that we assume that we can check the sorting order?
- /// <summary>
- /// Add all the items from another collection with an enumeration order that
- /// is increasing in the items.
- /// </summary>
- /// <exception cref="ArgumentException"> if the enumerated items turns out
- /// not to be in increasing order.</exception>
- /// <param name="items">The collection to add.</param>
- void AddSorted(SCG.IEnumerable<KeyValuePair<K, V>> items);
-
-
- /// <summary>
- /// Remove all items of this collection above or at a supplied threshold.
- /// </summary>
- /// <param name="low">The lower threshold (inclusive).</param>
- void RemoveRangeFrom(K low);
-
-
- /// <summary>
- /// Remove all items of this collection between two supplied thresholds.
- /// </summary>
- /// <param name="low">The lower threshold (inclusive).</param>
- /// <param name="hi">The upper threshold (exclusive).</param>
- void RemoveRangeFromTo(K low, K hi);
-
-
- /// <summary>
- /// Remove all items of this collection below a supplied threshold.
- /// </summary>
- /// <param name="hi">The upper threshold (exclusive).</param>
- void RemoveRangeTo(K hi);
- }
-
-
-
- /*******************************************************************/
- /*/// <summary>
- /// The type of an item comparer
- /// <i>Implementations of this interface must asure that the method is self-consistent
- /// and defines a sorting order on items, or state precise conditions under which this is true.</i>
- /// <i>Implementations <b>must</b> assure that repeated calls of
- /// the method to the same (in reference or binary identity sense) arguments
- /// will return values with the same sign (-1, 0 or +1), or state precise conditions
- /// under which the user
- /// can be assured repeated calls will return the same sign.</i>
- /// <i>Implementations of this interface must always return values from the method
- /// and never throw exceptions.</i>
- /// <i>This interface is identical to System.Collections.Generic.IComparer<T></i>
- /// </summary>
- public interface IComparer<T>
- {
- /// <summary>
- /// Compare two items with respect to this item comparer
- /// </summary>
- /// <param name="item1">First item</param>
- /// <param name="item2">Second item</param>
- /// <returns>Positive if item1 is greater than item2, 0 if they are equal, negative if item1 is less than item2</returns>
- int Compare(T item1, T item2);
- }
-
- /// <summary>
- /// The type of an item equalityComparer.
- /// <i>Implementations of this interface <b>must</b> assure that the methods are
- /// consistent, that is, that whenever two items i1 and i2 satisfies that Equals(i1,i2)
- /// returns true, then GetHashCode returns the same value for i1 and i2.</i>
- /// <i>Implementations of this interface <b>must</b> assure that repeated calls of
- /// the methods to the same (in reference or binary identity sense) arguments
- /// will return the same values, or state precise conditions under which the user
- /// can be assured repeated calls will return the same values.</i>
- /// <i>Implementations of this interface must always return values from the methods
- /// and never throw exceptions.</i>
- /// <i>This interface is similar in function to System.IKeyComparer<T></i>
- /// </summary>
- public interface SCG.IEqualityComparer<T>
- {
- /// <summary>
- /// Get the hash code with respect to this item equalityComparer
- /// </summary>
- /// <param name="item">The item</param>
- /// <returns>The hash code</returns>
- int GetHashCode(T item);
-
-
- /// <summary>
- /// Check if two items are equal with respect to this item equalityComparer
- /// </summary>
- /// <param name="item1">first item</param>
- /// <param name="item2">second item</param>
- /// <returns>True if equal</returns>
- bool Equals(T item1, T item2);
- }*/
-}
+/* + Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +using System; +using SCG = System.Collections.Generic; +namespace C5 +{ + /// <summary> + /// A generic collection, that can be enumerated backwards. + /// </summary> + public interface IDirectedEnumerable<T> : SCG.IEnumerable<T> + { + /// <summary> + /// Create a collection containing the same items as this collection, but + /// whose enumerator will enumerate the items backwards. The new collection + /// will become invalid if the original is modified. Method typically used as in + /// <code>foreach (T x in coll.Backwards()) {...}</code> + /// </summary> + /// <returns>The backwards collection.</returns> + IDirectedEnumerable<T> Backwards(); + + + /// <summary> + /// <code>Forwards</code> if same, else <code>Backwards</code> + /// </summary> + /// <value>The enumeration direction relative to the original collection.</value> + EnumerationDirection Direction { get;} + } + + /// <summary> + /// A generic collection that may be enumerated and can answer + /// efficiently how many items it contains. Like <code>IEnumerable<T></code>, + /// this interface does not prescribe any operations to initialize or update the + /// collection. The main usage for this interface is to be the return type of + /// query operations on generic collection. + /// </summary> + public interface ICollectionValue<T> : SCG.IEnumerable<T>, IShowable + { + /// <summary> + /// A flag bitmap of the events subscribable to by this collection. + /// </summary> + /// <value></value> + EventTypeEnum ListenableEvents { get;} + + /// <summary> + /// A flag bitmap of the events currently subscribed to by this collection. + /// </summary> + /// <value></value> + EventTypeEnum ActiveEvents { get;} + + /// <summary> + /// The change event. Will be raised for every change operation on the collection. + /// </summary> + event CollectionChangedHandler<T> CollectionChanged; + + /// <summary> + /// The change event. Will be raised for every clear operation on the collection. + /// </summary> + event CollectionClearedHandler<T> CollectionCleared; + + /// <summary> + /// The item added event. Will be raised for every individual addition to the collection. + /// </summary> + event ItemsAddedHandler<T> ItemsAdded; + + /// <summary> + /// The item inserted event. Will be raised for every individual insertion to the collection. + /// </summary> + event ItemInsertedHandler<T> ItemInserted; + + /// <summary> + /// The item removed event. Will be raised for every individual removal from the collection. + /// </summary> + event ItemsRemovedHandler<T> ItemsRemoved; + + /// <summary> + /// The item removed at event. Will be raised for every individual removal at from the collection. + /// </summary> + event ItemRemovedAtHandler<T> ItemRemovedAt; + + /// <summary> + /// + /// </summary> + /// <value>True if this collection is empty.</value> + bool IsEmpty { get;} + + /// <summary> + /// </summary> + /// <value>The number of items in this collection</value> + int Count { get;} + + /// <summary> + /// The value is symbolic indicating the type of asymptotic complexity + /// in terms of the size of this collection (worst-case or amortized as + /// relevant). + /// </summary> + /// <value>A characterization of the speed of the + /// <code>Count</code> property in this collection.</value> + Speed CountSpeed { get;} + + /// <summary> + /// Copy the items of this collection to a contiguous part of an array. + /// </summary> + /// <param name="array">The array to copy to</param> + /// <param name="index">The index at which to copy the first item</param> + void CopyTo(T[] array, int index); + + /// <summary> + /// Create an array with the items of this collection (in the same order as an + /// enumerator would output them). + /// </summary> + /// <returns>The array</returns> + T[] ToArray(); + + /// <summary> + /// Apply a delegate to all items of this collection. + /// </summary> + /// <param name="action">The delegate to apply</param> + void Apply(Act<T> action); + + + /// <summary> + /// Check if there exists an item that satisfies a + /// specific predicate in this collection. + /// </summary> + /// <param name="predicate">A delegate + /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param> + /// <returns>True is such an item exists</returns> + bool Exists(Fun<T, bool> predicate); + + /// <summary> + /// Check if there exists an item that satisfies a + /// specific predicate in this collection and return the first one in enumeration order. + /// </summary> + /// <param name="predicate">A delegate + /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param> + /// <param name="item"></param> + /// <returns>True is such an item exists</returns> + bool Find(Fun<T, bool> predicate, out T item); + + + /// <summary> + /// Check if all items in this collection satisfies a specific predicate. + /// </summary> + /// <param name="predicate">A delegate + /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param> + /// <returns>True if all items satisfies the predicate</returns> + bool All(Fun<T, bool> predicate); + + /// <summary> + /// Choose some item of this collection. + /// <para>Implementations must assure that the item + /// returned may be efficiently removed.</para> + /// <para>Implementors may decide to implement this method in a way such that repeated + /// calls do not necessarily give the same result, i.e. so that the result of the following + /// test is undetermined: + /// <code>coll.Choose() == coll.Choose()</code></para> + /// </summary> + /// <exception cref="NoSuchItemException">if collection is empty.</exception> + /// <returns></returns> + T Choose(); + + /// <summary> + /// Create an enumerable, enumerating the items of this collection that satisfies + /// a certain condition. + /// </summary> + /// <param name="filter">The T->bool filter delegate defining the condition</param> + /// <returns>The filtered enumerable</returns> + SCG.IEnumerable<T> Filter(Fun<T, bool> filter); + } + + + + /// <summary> + /// A sized generic collection, that can be enumerated backwards. + /// </summary> + public interface IDirectedCollectionValue<T> : ICollectionValue<T>, IDirectedEnumerable<T> + { + /// <summary> + /// Create a collection containing the same items as this collection, but + /// whose enumerator will enumerate the items backwards. The new collection + /// will become invalid if the original is modified. Method typically used as in + /// <code>foreach (T x in coll.Backwards()) {...}</code> + /// </summary> + /// <returns>The backwards collection.</returns> + new IDirectedCollectionValue<T> Backwards(); + + /// <summary> + /// Check if there exists an item that satisfies a + /// specific predicate in this collection and return the first one in enumeration order. + /// </summary> + /// <param name="predicate">A delegate + /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param> + /// <param name="item"></param> + /// <returns>True is such an item exists</returns> + bool FindLast(Fun<T, bool> predicate, out T item); + } + + + /// <summary> + /// A generic collection to which one may add items. This is just the intersection + /// of the main stream generic collection interfaces and the priority queue interface, + /// <see cref="T:C5.ICollection`1"/> and <see cref="T:C5.IPriorityQueue`1"/>. + /// </summary> + public interface IExtensible<T> : ICollectionValue<T>, ICloneable + { + /// <summary> + /// If true any call of an updating operation will throw an + /// <code>ReadOnlyCollectionException</code> + /// </summary> + /// <value>True if this collection is read-only.</value> + bool IsReadOnly { get;} + + //TODO: wonder where the right position of this is + /// <summary> + /// + /// </summary> + /// <value>False if this collection has set semantics, true if bag semantics.</value> + bool AllowsDuplicates { get;} + + //TODO: wonder where the right position of this is. And the semantics. + /// <summary> + /// (Here should be a discussion of the role of equalityComparers. Any ). + /// </summary> + /// <value>The equalityComparer used by this collection to check equality of items. + /// Or null (????) if collection does not check equality at all or uses a comparer.</value> + SCG.IEqualityComparer<T> EqualityComparer { get;} + + //ItemEqualityTypeEnum ItemEqualityType {get ;} + + //TODO: find a good name + + /// <summary> + /// By convention this is true for any collection with set semantics. + /// </summary> + /// <value>True if only one representative of a group of equal items + /// is kept in the collection together with the total count.</value> + bool DuplicatesByCounting { get;} + + /// <summary> + /// Add an item to this collection if possible. If this collection has set + /// semantics, the item will be added if not already in the collection. If + /// bag semantics, the item will always be added. + /// </summary> + /// <param name="item">The item to add.</param> + /// <returns>True if item was added.</returns> + bool Add(T item); + + /// <summary> + /// Add the elements from another collection with a more specialized item type + /// to this collection. If this + /// collection has set semantics, only items not already in the collection + /// will be added. + /// </summary> + /// <typeparam name="U">The type of items to add</typeparam> + /// <param name="items">The items to add</param> + void AddAll<U>(SCG.IEnumerable<U> items) where U : T; + + //void Clear(); // for priority queue + //int Count why not? + /// <summary> + /// Check the integrity of the internal data structures of this collection. + /// <i>This is only relevant for developers of the library</i> + /// </summary> + /// <returns>True if check was passed.</returns> + bool Check(); + } + + /// <summary> + /// The simplest interface of a main stream generic collection + /// with lookup, insertion and removal operations. + /// </summary> + public interface ICollection<T> : IExtensible<T>, SCG.ICollection<T> + { + //This is somewhat similar to the RandomAccess marker itf in java + /// <summary> + /// The value is symbolic indicating the type of asymptotic complexity + /// in terms of the size of this collection (worst-case or amortized as + /// relevant). + /// <para>See <see cref="T:C5.Speed"/> for the set of symbols.</para> + /// </summary> + /// <value>A characterization of the speed of lookup operations + /// (<code>Contains()</code> etc.) of the implementation of this collection.</value> + Speed ContainsSpeed { get;} + + /// <summary> + /// </summary> + /// <value>The number of items in this collection</value> + new int Count { get; } + + /// <summary> + /// If true any call of an updating operation will throw an + /// <code>ReadOnlyCollectionException</code> + /// </summary> + /// <value>True if this collection is read-only.</value> + new bool IsReadOnly { get; } + + /// <summary> + /// Add an item to this collection if possible. If this collection has set + /// semantics, the item will be added if not already in the collection. If + /// bag semantics, the item will always be added. + /// </summary> + /// <param name="item">The item to add.</param> + /// <returns>True if item was added.</returns> + new bool Add(T item); + + /// <summary> + /// Copy the items of this collection to a contiguous part of an array. + /// </summary> + /// <param name="array">The array to copy to</param> + /// <param name="index">The index at which to copy the first item</param> + new void CopyTo(T[] array, int index); + + /// <summary> + /// The unordered collection hashcode is defined as the sum of + /// <code>h(hashcode(item))</code> over the items + /// of the collection, where the function <code>h</code> is a function from + /// int to int of the form <code> t -> (a0*t+b0)^(a1*t+b1)^(a2*t+b2)</code>, where + /// the ax and bx are the same for all collection classes. + /// <para>The current implementation uses fixed values for the ax and bx, + /// specified as constants in the code.</para> + /// </summary> + /// <returns>The unordered hashcode of this collection.</returns> + int GetUnsequencedHashCode(); + + + /// <summary> + /// Compare the contents of this collection to another one without regards to + /// the sequence order. The comparison will use this collection's itemequalityComparer + /// to compare individual items. + /// </summary> + /// <param name="otherCollection">The collection to compare to.</param> + /// <returns>True if this collection and that contains the same items.</returns> + bool UnsequencedEquals(ICollection<T> otherCollection); + + + /// <summary> + /// Check if this collection contains (an item equivalent to according to the + /// itemequalityComparer) a particular value. + /// </summary> + /// <param name="item">The value to check for.</param> + /// <returns>True if the items is in this collection.</returns> + new bool Contains(T item); + + + /// <summary> + /// Count the number of items of the collection equal to a particular value. + /// Returns 0 if and only if the value is not in the collection. + /// </summary> + /// <param name="item">The value to count.</param> + /// <returns>The number of copies found.</returns> + int ContainsCount(T item); + + + /// <summary> + /// + /// </summary> + /// <returns></returns> + ICollectionValue<T> UniqueItems(); + + /// <summary> + /// + /// </summary> + /// <returns></returns> + ICollectionValue<KeyValuePair<T, int>> ItemMultiplicities(); + + /// <summary> + /// Check whether this collection contains all the values in another collection. + /// If this collection has bag semantics (<code>AllowsDuplicates==true</code>) + /// the check is made with respect to multiplicities, else multiplicities + /// are not taken into account. + /// </summary> + /// <param name="items">The </param> + /// <typeparam name="U"></typeparam> + /// <returns>True if all values in <code>items</code>is in this collection.</returns> + bool ContainsAll<U>(SCG.IEnumerable<U> items) where U : T; + + + /// <summary> + /// Check if this collection contains an item equivalent according to the + /// itemequalityComparer to a particular value. If so, return in the ref argument (a + /// binary copy of) the actual value found. + /// </summary> + /// <param name="item">The value to look for.</param> + /// <returns>True if the items is in this collection.</returns> + bool Find(ref T item); + + + //This should probably just be bool Add(ref T item); !!! + /// <summary> + /// Check if this collection contains an item equivalent according to the + /// itemequalityComparer to a particular value. If so, return in the ref argument (a + /// binary copy of) the actual value found. Else, add the item to the collection. + /// </summary> + /// <param name="item">The value to look for.</param> + /// <returns>True if the item was found (hence not added).</returns> + bool FindOrAdd(ref T item); + + + /// <summary> + /// Check if this collection contains an item equivalent according to the + /// itemequalityComparer to a particular value. If so, update the item in the collection + /// with a (binary copy of) the supplied value. If the collection has bag semantics, + /// it depends on the value of DuplicatesByCounting if this updates all equivalent copies in + /// the collection or just one. + /// </summary> + /// <param name="item">Value to update.</param> + /// <returns>True if the item was found and hence updated.</returns> + bool Update(T item); + + /// <summary> + /// Check if this collection contains an item equivalent according to the + /// itemequalityComparer to a particular value. If so, update the item in the collection + /// with a (binary copy of) the supplied value. If the collection has bag semantics, + /// it depends on the value of DuplicatesByCounting if this updates all equivalent copies in + /// the collection or just one. + /// </summary> + /// <param name="item">Value to update.</param> + /// <param name="olditem">On output the olditem, if found.</param> + /// <returns>True if the item was found and hence updated.</returns> + bool Update(T item, out T olditem); + + + /// <summary> + /// Check if this collection contains an item equivalent according to the + /// itemequalityComparer to a particular value. If so, update the item in the collection + /// to with a binary copy of the supplied value; else add the value to the collection. + /// </summary> + /// <param name="item">Value to add or update.</param> + /// <returns>True if the item was found and updated (hence not added).</returns> + bool UpdateOrAdd(T item); + + + /// <summary> + /// Check if this collection contains an item equivalent according to the + /// itemequalityComparer to a particular value. If so, update the item in the collection + /// to with a binary copy of the supplied value; else add the value to the collection. + /// </summary> + /// <param name="item">Value to add or update.</param> + /// <param name="olditem">On output the olditem, if found.</param> + /// <returns>True if the item was found and updated (hence not added).</returns> + bool UpdateOrAdd(T item, out T olditem); + + /// <summary> + /// Remove a particular item from this collection. If the collection has bag + /// semantics only one copy equivalent to the supplied item is removed. + /// </summary> + /// <param name="item">The value to remove.</param> + /// <returns>True if the item was found (and removed).</returns> + new bool Remove(T item); + + + /// <summary> + /// Remove a particular item from this collection if found. If the collection + /// has bag semantics only one copy equivalent to the supplied item is removed, + /// which one is implementation dependent. + /// If an item was removed, report a binary copy of the actual item removed in + /// the argument. + /// </summary> + /// <param name="item">The value to remove.</param> + /// <param name="removeditem">The value removed if any.</param> + /// <returns>True if the item was found (and removed).</returns> + bool Remove(T item, out T removeditem); + + + /// <summary> + /// Remove all items equivalent to a given value. + /// </summary> + /// <param name="item">The value to remove.</param> + void RemoveAllCopies(T item); + + + /// <summary> + /// Remove all items in another collection from this one. If this collection + /// has bag semantics, take multiplicities into account. + /// </summary> + /// <typeparam name="U"></typeparam> + /// <param name="items">The items to remove.</param> + void RemoveAll<U>(SCG.IEnumerable<U> items) where U : T; + + //void RemoveAll(Fun<T, bool> predicate); + + /// <summary> + /// Remove all items from this collection. + /// </summary> + new void Clear(); + + + /// <summary> + /// Remove all items not in some other collection from this one. If this collection + /// has bag semantics, take multiplicities into account. + /// </summary> + /// <typeparam name="U"></typeparam> + /// <param name="items">The items to retain.</param> + void RetainAll<U>(SCG.IEnumerable<U> items) where U : T; + + //void RetainAll(Fun<T, bool> predicate); + //IDictionary<T> UniqueItems() + } + + + + /// <summary> + /// An editable collection maintaining a definite sequence order of the items. + /// + /// <i>Implementations of this interface must compute the hash code and + /// equality exactly as prescribed in the method definitions in order to + /// be consistent with other collection classes implementing this interface.</i> + /// <i>This interface is usually implemented by explicit interface implementation, + /// not as ordinary virtual methods.</i> + /// </summary> + public interface ISequenced<T> : ICollection<T>, IDirectedCollectionValue<T> + { + /// <summary> + /// The hashcode is defined as <code>h(...h(h(h(x1),x2),x3),...,xn)</code> for + /// <code>h(a,b)=CONSTANT*a+b</code> and the x's the hash codes of the items of + /// this collection. + /// </summary> + /// <returns>The sequence order hashcode of this collection.</returns> + int GetSequencedHashCode(); + + + /// <summary> + /// Compare this sequenced collection to another one in sequence order. + /// </summary> + /// <param name="otherCollection">The sequenced collection to compare to.</param> + /// <returns>True if this collection and that contains equal (according to + /// this collection's itemequalityComparer) in the same sequence order.</returns> + bool SequencedEquals(ISequenced<T> otherCollection); + } + + + + /// <summary> + /// A sequenced collection, where indices of items in the order are maintained + /// </summary> + public interface IIndexed<T> : ISequenced<T> + { + /// <summary> + /// </summary> + /// <exception cref="IndexOutOfRangeException"> if <code>index</code> is negative or + /// >= the size of the collection.</exception> + /// <value>The <code>index</code>'th item of this list.</value> + /// <param name="index">the index to lookup</param> + T this[int index] { get;} + + /// <summary> + /// + /// </summary> + /// <value></value> + Speed IndexingSpeed { get;} + + /// <summary> + /// </summary> + /// <exception cref="ArgumentOutOfRangeException"></exception> + /// <value>The directed collection of items in a specific index interval.</value> + /// <param name="start">The low index of the interval (inclusive).</param> + /// <param name="count">The size of the range.</param> + IDirectedCollectionValue<T> this[int start, int count] { get;} + + + /// <summary> + /// Searches for an item in the list going forwards from the start. + /// </summary> + /// <param name="item">Item to search for.</param> + /// <returns>Index of item from start. A negative number if item not found, + /// namely the one's complement of the index at which the Add operation would put the item.</returns> + int IndexOf(T item); + + + /// <summary> + /// Searches for an item in the list going backwards from the end. + /// </summary> + /// <param name="item">Item to search for.</param> + /// <returns>Index of of item from the end. A negative number if item not found, + /// namely the two-complement of the index at which the Add operation would put the item.</returns> + int LastIndexOf(T item); + + /// <summary> + /// Check if there exists an item that satisfies a + /// specific predicate in this collection and return the index of the first one. + /// </summary> + /// <param name="predicate">A delegate + /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param> + /// <returns>the index, if found, a negative value else</returns> + int FindIndex(Fun<T, bool> predicate); + + /// <summary> + /// Check if there exists an item that satisfies a + /// specific predicate in this collection and return the index of the last one. + /// </summary> + /// <param name="predicate">A delegate + /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param> + /// <returns>the index, if found, a negative value else</returns> + int FindLastIndex(Fun<T, bool> predicate); + + + /// <summary> + /// Remove the item at a specific position of the list. + /// </summary> + /// <exception cref="IndexOutOfRangeException"> if <code>index</code> is negative or + /// >= the size of the collection.</exception> + /// <param name="index">The index of the item to remove.</param> + /// <returns>The removed item.</returns> + T RemoveAt(int index); + + + /// <summary> + /// Remove all items in an index interval. + /// </summary> + /// <exception cref="ArgumentOutOfRangeException"> if start or count + /// is negative or start+count > the size of the collection.</exception> + /// <param name="start">The index of the first item to remove.</param> + /// <param name="count">The number of items to remove.</param> + void RemoveInterval(int start, int count); + } + + //TODO: decide if this should extend ICollection + /// <summary> + /// The interface describing the operations of a LIFO stack data structure. + /// </summary> + /// <typeparam name="T">The item type</typeparam> + public interface IStack<T> : IDirectedCollectionValue<T> + { + /// <summary> + /// + /// </summary> + /// <value></value> + bool AllowsDuplicates { get;} + /// <summary> + /// Get the <code>index</code>'th element of the stack. The bottom of the stack has index 0. + /// </summary> + /// <param name="index"></param> + /// <returns></returns> + T this[int index] { get;} + /// <summary> + /// Push an item to the top of the stack. + /// </summary> + /// <param name="item">The item</param> + void Push(T item); + /// <summary> + /// Pop the item at the top of the stack from the stack. + /// </summary> + /// <returns>The popped item.</returns> + T Pop(); + } + + /// <summary> + /// The interface describing the operations of a FIFO queue data structure. + /// </summary> + /// <typeparam name="T">The item type</typeparam> + public interface IQueue<T> : IDirectedCollectionValue<T> + { + /// <summary> + /// + /// </summary> + /// <value></value> + bool AllowsDuplicates { get;} + /// <summary> + /// Get the <code>index</code>'th element of the queue. The front of the queue has index 0. + /// </summary> + /// <param name="index"></param> + /// <returns></returns> + T this[int index] { get;} + /// <summary> + /// Enqueue an item at the back of the queue. + /// </summary> + /// <param name="item">The item</param> + void Enqueue(T item); + /// <summary> + /// Dequeue an item from the front of the queue. + /// </summary> + /// <returns>The item</returns> + T Dequeue(); + } + + + /// <summary> + /// This is an indexed collection, where the item order is chosen by + /// the user at insertion time. + /// + /// NBNBNB: we need a description of the view functionality here! + /// </summary> + public interface IList<T> : IIndexed<T>, IDisposable, SCG.IList<T>, System.Collections.IList + { + /// <summary> + /// </summary> + /// <exception cref="NoSuchItemException"> if this list is empty.</exception> + /// <value>The first item in this list.</value> + T First { get;} + + /// <summary> + /// </summary> + /// <exception cref="NoSuchItemException"> if this list is empty.</exception> + /// <value>The last item in this list.</value> + T Last { get;} + + /// <summary> + /// Since <code>Add(T item)</code> always add at the end of the list, + /// this describes if list has FIFO or LIFO semantics. + /// </summary> + /// <value>True if the <code>Remove()</code> operation removes from the + /// start of the list, false if it removes from the end.</value> + bool FIFO { get; set;} + + /// <summary> + /// + /// </summary> + bool IsFixedSize { get; } + + /// <summary> + /// On this list, this indexer is read/write. + /// </summary> + /// <exception cref="IndexOutOfRangeException"> if index is negative or + /// >= the size of the collection.</exception> + /// <value>The index'th item of this list.</value> + /// <param name="index">The index of the item to fetch or store.</param> + new T this[int index] { get; set;} + + #region Ambiguous calls when extending SCG.IList<T> + + #region SCG.ICollection<T> + /// <summary> + /// + /// </summary> + new int Count { get; } + + /// <summary> + /// + /// </summary> + new bool IsReadOnly { get; } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <returns></returns> + new bool Add(T item); + + /// <summary> + /// + /// </summary> + new void Clear(); + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <returns></returns> + new bool Contains(T item); + + /// <summary> + /// + /// </summary> + /// <param name="array"></param> + /// <param name="index"></param> + new void CopyTo(T[] array, int index); + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <returns></returns> + new bool Remove(T item); + + #endregion + + #region SCG.IList<T> proper + + /// <summary> + /// Searches for an item in the list going forwards from the start. + /// </summary> + /// <param name="item">Item to search for.</param> + /// <returns>Index of item from start. A negative number if item not found, + /// namely the one's complement of the index at which the Add operation would put the item.</returns> + new int IndexOf(T item); + + /// <summary> + /// Remove the item at a specific position of the list. + /// </summary> + /// <exception cref="IndexOutOfRangeException"> if <code>index</code> is negative or + /// >= the size of the collection.</exception> + /// <param name="index">The index of the item to remove.</param> + /// <returns>The removed item.</returns> + new T RemoveAt(int index); + + #endregion + + #endregion + + /*/// <summary> + /// Insert an item at a specific index location in this list. + /// </summary> + /// <exception cref="IndexOutOfRangeException"> if <code>index</code> is negative or + /// > the size of the collection.</exception> + /// <exception cref="DuplicateNotAllowedException"> if the list has + /// <code>AllowsDuplicates==false</code> and the item is + /// already in the list.</exception> + /// <param name="index">The index at which to insert.</param> + /// <param name="item">The item to insert.</param> + void Insert(int index, T item);*/ + + /// <summary> + /// Insert an item at the end of a compatible view, used as a pointer. + /// <para>The <code>pointer</code> must be a view on the same list as + /// <code>this</code> and the endpoitn of <code>pointer</code> must be + /// a valid insertion point of <code>this</code></para> + /// </summary> + /// <exception cref="IncompatibleViewException">If <code>pointer</code> + /// is not a view on the same list as <code>this</code></exception> + /// <exception cref="IndexOutOfRangeException"><b>??????</b> if the endpoint of + /// <code>pointer</code> is not inside <code>this</code></exception> + /// <exception cref="DuplicateNotAllowedException"> if the list has + /// <code>AllowsDuplicates==false</code> and the item is + /// already in the list.</exception> + /// <param name="pointer"></param> + /// <param name="item"></param> + void Insert(IList<T> pointer, T item); + + /// <summary> + /// Insert an item at the front of this list. + /// <exception cref="DuplicateNotAllowedException"/> if the list has + /// <code>AllowsDuplicates==false</code> and the item is + /// already in the list. + /// </summary> + /// <param name="item">The item to insert.</param> + void InsertFirst(T item); + + /// <summary> + /// Insert an item at the back of this list. + /// <exception cref="DuplicateNotAllowedException"/> if the list has + /// <code>AllowsDuplicates==false</code> and the item is + /// already in the list. + /// </summary> + /// <param name="item">The item to insert.</param> + void InsertLast(T item); + + /// <summary> + /// Insert into this list all items from an enumerable collection starting + /// at a particular index. + /// </summary> + /// <exception cref="IndexOutOfRangeException"> if <code>index</code> is negative or + /// > the size of the collection.</exception> + /// <exception cref="DuplicateNotAllowedException"> if the list has + /// <code>AllowsDuplicates==false</code> and one of the items to insert is + /// already in the list.</exception> + /// <param name="index">Index to start inserting at</param> + /// <param name="items">Items to insert</param> + /// <typeparam name="U"></typeparam> + void InsertAll<U>(int index, SCG.IEnumerable<U> items) where U : T; + + /// <summary> + /// Create a new list consisting of the items of this list satisfying a + /// certain predicate. + /// </summary> + /// <param name="filter">The filter delegate defining the predicate.</param> + /// <returns>The new list.</returns> + IList<T> FindAll(Fun<T, bool> filter); + + /// <summary> + /// Create a new list consisting of the results of mapping all items of this + /// list. The new list will use the default equalityComparer for the item type V. + /// </summary> + /// <typeparam name="V">The type of items of the new list</typeparam> + /// <param name="mapper">The delegate defining the map.</param> + /// <returns>The new list.</returns> + IList<V> Map<V>(Fun<T, V> mapper); + + /// <summary> + /// Create a new list consisting of the results of mapping all items of this + /// list. The new list will use a specified equalityComparer for the item type. + /// </summary> + /// <typeparam name="V">The type of items of the new list</typeparam> + /// <param name="mapper">The delegate defining the map.</param> + /// <param name="equalityComparer">The equalityComparer to use for the new list</param> + /// <returns>The new list.</returns> + IList<V> Map<V>(Fun<T, V> mapper, SCG.IEqualityComparer<V> equalityComparer); + + /// <summary> + /// Remove one item from the list: from the front if <code>FIFO</code> + /// is true, else from the back. + /// <exception cref="NoSuchItemException"/> if this list is empty. + /// </summary> + /// <returns>The removed item.</returns> + T Remove(); + + /// <summary> + /// Remove one item from the front of the list. + /// <exception cref="NoSuchItemException"/> if this list is empty. + /// </summary> + /// <returns>The removed item.</returns> + T RemoveFirst(); + + /// <summary> + /// Remove one item from the back of the list. + /// <exception cref="NoSuchItemException"/> if this list is empty. + /// </summary> + /// <returns>The removed item.</returns> + T RemoveLast(); + + /// <summary> + /// Create a list view on this list. + /// <exception cref="ArgumentOutOfRangeException"/> if the view would not fit into + /// this list. + /// </summary> + /// <param name="start">The index in this list of the start of the view.</param> + /// <param name="count">The size of the view.</param> + /// <returns>The new list view.</returns> + IList<T> View(int start, int count); + + /// <summary> + /// Create a list view on this list containing the (first) occurrence of a particular item. + /// <exception cref="NoSuchItemException"/> if the item is not in this list. + /// </summary> + /// <param name="item">The item to find.</param> + /// <returns>The new list view.</returns> + IList<T> ViewOf(T item); + + /// <summary> + /// Create a list view on this list containing the last occurrence of a particular item. + /// <exception cref="NoSuchItemException"/> if the item is not in this list. + /// </summary> + /// <param name="item">The item to find.</param> + /// <returns>The new list view.</returns> + IList<T> LastViewOf(T item); + + /// <summary> + /// Null if this list is not a view. + /// </summary> + /// <value>Underlying list for view.</value> + IList<T> Underlying { get;} + + /// <summary> + /// </summary> + /// <value>Offset for this list view or 0 for an underlying list.</value> + int Offset { get;} + + /// <summary> + /// + /// </summary> + /// <value></value> + bool IsValid { get;} + + /// <summary> + /// Slide this list view along the underlying list. + /// </summary> + /// <exception cref="NotAViewException"> if this list is not a view.</exception> + /// <exception cref="ArgumentOutOfRangeException"> if the operation + /// would bring either end of the view outside the underlying list.</exception> + /// <param name="offset">The signed amount to slide: positive to slide + /// towards the end.</param> + IList<T> Slide(int offset); + + /// <summary> + /// Slide this list view along the underlying list, changing its size. + /// + /// </summary> + /// <exception cref="NotAViewException"> if this list is not a view.</exception> + /// <exception cref="ArgumentOutOfRangeException"> if the operation + /// would bring either end of the view outside the underlying list.</exception> + /// <param name="offset">The signed amount to slide: positive to slide + /// towards the end.</param> + /// <param name="size">The new size of the view.</param> + IList<T> Slide(int offset, int size); + + /// <summary> + /// + /// </summary> + /// <param name="offset"></param> + /// <returns></returns> + bool TrySlide(int offset); + + /// <summary> + /// + /// </summary> + /// <param name="offset"></param> + /// <param name="size"></param> + /// <returns></returns> + bool TrySlide(int offset, int size); + + /// <summary> + /// + /// <para>Returns null if <code>otherView</code> is strictly to the left of this view</para> + /// </summary> + /// <param name="otherView"></param> + /// <exception cref="IncompatibleViewException">If otherView does not have the same underlying list as this</exception> + /// <exception cref="ArgumentOutOfRangeException">If <code>otherView</code> is strictly to the left of this view</exception> + /// <returns></returns> + IList<T> Span(IList<T> otherView); + + /// <summary> + /// Reverse the list so the items are in the opposite sequence order. + /// </summary> + void Reverse(); + + /// <summary> + /// Check if this list is sorted according to the default sorting order + /// for the item type T, as defined by the <see cref="T:C5.Comparer`1"/> class + /// </summary> + /// <exception cref="NotComparableException">if T is not comparable</exception> + /// <returns>True if the list is sorted, else false.</returns> + bool IsSorted(); + + /// <summary> + /// Check if this list is sorted according to a specific sorting order. + /// </summary> + /// <param name="comparer">The comparer defining the sorting order.</param> + /// <returns>True if the list is sorted, else false.</returns> + bool IsSorted(SCG.IComparer<T> comparer); + + /// <summary> + /// Sort the items of the list according to the default sorting order + /// for the item type T, as defined by the <see cref="T:C5.Comparer`1"/> class + /// </summary> + /// <exception cref="NotComparableException">if T is not comparable</exception> + void Sort(); + + /// <summary> + /// Sort the items of the list according to a specified sorting order. + /// <para>The sorting does not perform duplicate elimination or identify items + /// according to the comparer or itemequalityComparer. I.e. the list as an + /// unsequenced collection with binary equality, will not change. + /// </para> + /// </summary> + /// <param name="comparer">The comparer defining the sorting order.</param> + void Sort(SCG.IComparer<T> comparer); + + + /// <summary> + /// Randomly shuffle the items of this list. + /// </summary> + void Shuffle(); + + + /// <summary> + /// Shuffle the items of this list according to a specific random source. + /// </summary> + /// <param name="rnd">The random source.</param> + void Shuffle(Random rnd); + } + + + /// <summary> + /// The base type of a priority queue handle + /// </summary> + /// <typeparam name="T"></typeparam> + public interface IPriorityQueueHandle<T> + { + //TODO: make abstract and prepare for double dispatch: + //public virtual bool Delete(IPriorityQueue<T> q) { throw new InvalidFooException();} + //bool Replace(T item); + } + + + /// <summary> + /// A generic collection of items prioritized by a comparison (order) relation. + /// Supports adding items and reporting or removing extremal elements. + /// <para> + /// + /// </para> + /// When adding an item, the user may choose to have a handle allocated for this item in the queue. + /// The resulting handle may be used for deleting the item even if not extremal, and for replacing the item. + /// A priority queue typically only holds numeric priorities associated with some objects + /// maintained separately in other collection objects. + /// </summary> + public interface IPriorityQueue<T> : IExtensible<T> + { + /// <summary> + /// Find the current least item of this priority queue. + /// </summary> + /// <returns>The least item.</returns> + T FindMin(); + + + /// <summary> + /// Remove the least item from this priority queue. + /// </summary> + /// <returns>The removed item.</returns> + T DeleteMin(); + + + /// <summary> + /// Find the current largest item of this priority queue. + /// </summary> + /// <returns>The largest item.</returns> + T FindMax(); + + + /// <summary> + /// Remove the largest item from this priority queue. + /// </summary> + /// <returns>The removed item.</returns> + T DeleteMax(); + + /// <summary> + /// The comparer object supplied at creation time for this collection + /// </summary> + /// <value>The comparer</value> + SCG.IComparer<T> Comparer { get;} + /// <summary> + /// Get or set the item corresponding to a handle. Throws exceptions on + /// invalid handles. + /// </summary> + /// <param name="handle"></param> + /// <returns></returns> + T this[IPriorityQueueHandle<T> handle] { get; set;} + + /// <summary> + /// Check if the entry corresponding to a handle is in the priority queue. + /// </summary> + /// <param name="handle"></param> + /// <param name="item"></param> + /// <returns></returns> + bool Find(IPriorityQueueHandle<T> handle, out T item); + + /// <summary> + /// Add an item to the priority queue, receiving a + /// handle for the item in the queue, + /// or reusing an existing unused handle. + /// </summary> + /// <param name="handle">On output: a handle for the added item. + /// On input: null for allocating a new handle, or a currently unused handle for reuse. + /// A handle for reuse must be compatible with this priority queue, + /// by being created by a priority queue of the same runtime type, but not + /// necessarily the same priority queue object.</param> + /// <param name="item"></param> + /// <returns></returns> + bool Add(ref IPriorityQueueHandle<T> handle, T item); + + /// <summary> + /// Delete an item with a handle from a priority queue + /// </summary> + /// <param name="handle">The handle for the item. The handle will be invalidated, but reusable.</param> + /// <returns>The deleted item</returns> + T Delete(IPriorityQueueHandle<T> handle); + + /// <summary> + /// Replace an item with a handle in a priority queue with a new item. + /// Typically used for changing the priority of some queued object. + /// </summary> + /// <param name="handle">The handle for the old item</param> + /// <param name="item">The new item</param> + /// <returns>The old item</returns> + T Replace(IPriorityQueueHandle<T> handle, T item); + + /// <summary> + /// Find the current least item of this priority queue. + /// </summary> + /// <param name="handle">On return: the handle of the item.</param> + /// <returns>The least item.</returns> + T FindMin(out IPriorityQueueHandle<T> handle); + + /// <summary> + /// Find the current largest item of this priority queue. + /// </summary> + /// <param name="handle">On return: the handle of the item.</param> + /// <returns>The largest item.</returns> + + T FindMax(out IPriorityQueueHandle<T> handle); + + /// <summary> + /// Remove the least item from this priority queue. + /// </summary> + /// <param name="handle">On return: the handle of the removed item.</param> + /// <returns>The removed item.</returns> + + T DeleteMin(out IPriorityQueueHandle<T> handle); + + /// <summary> + /// Remove the largest item from this priority queue. + /// </summary> + /// <param name="handle">On return: the handle of the removed item.</param> + /// <returns>The removed item.</returns> + T DeleteMax(out IPriorityQueueHandle<T> handle); + } + + + + /// <summary> + /// A sorted collection, i.e. a collection where items are maintained and can be searched for in sorted order. + /// Thus the sequence order is given as a sorting order. + /// + /// <para>The sorting order is defined by a comparer, an object of type IComparer<T> + /// (<see cref="T:C5.IComparer`1"/>). Implementors of this interface will normally let the user + /// define the comparer as an argument to a constructor. + /// Usually there will also be constructors without a comparer argument, in which case the + /// comparer should be the defalt comparer for the item type, <see cref="P:C5.Comparer`1.Default"/>.</para> + /// + /// <para>The comparer of the sorted collection is available as the <code>Comparer</code> property + /// (<see cref="P:C5.ISorted`1.Comparer"/>).</para> + /// + /// <para>The methods are grouped according to + /// <list> + /// <item>Extrema: report or report and delete an extremal item. This is reminiscent of simplified priority queues.</item> + /// <item>Nearest neighbor: report predecessor or successor in the collection of an item. Cut belongs to this group.</item> + /// <item>Range: report a view of a range of elements or remove all elements in a range.</item> + /// <item>AddSorted: add a collection of items known to be sorted in the same order (should be faster) (to be removed?)</item> + /// </list> + /// </para> + /// + /// <para>Since this interface extends ISequenced<T>, sorted collections will also have an + /// item equalityComparer (<see cref="P:C5.IExtensible`1.EqualityComparer"/>). This equalityComparer will not be used in connection with + /// the inner workings of the sorted collection, but will be used if the sorted collection is used as + /// an item in a collection of unsequenced or sequenced collections, + /// (<see cref="T:C5.ICollection`1"/> and <see cref="T:C5.ISequenced`1"/>)</para> + /// + /// <para>Note that code may check if two sorted collections has the same sorting order + /// by checking if the Comparer properties are equal. This is done a few places in this library + /// for optimization purposes.</para> + /// </summary> + public interface ISorted<T> : ISequenced<T> + { + /// <summary> + /// Find the current least item of this sorted collection. + /// </summary> + /// <exception cref="NoSuchItemException"> if the collection is empty.</exception> + /// <returns>The least item.</returns> + T FindMin(); + + + /// <summary> + /// Remove the least item from this sorted collection. + /// </summary> + /// <exception cref="NoSuchItemException"> if the collection is empty.</exception> + /// <returns>The removed item.</returns> + T DeleteMin(); + + + /// <summary> + /// Find the current largest item of this sorted collection. + /// </summary> + /// <exception cref="NoSuchItemException"> if the collection is empty.</exception> + /// <returns>The largest item.</returns> + T FindMax(); + + + /// <summary> + /// Remove the largest item from this sorted collection. + /// </summary> + /// <exception cref="NoSuchItemException"> if the collection is empty.</exception> + /// <returns>The removed item.</returns> + T DeleteMax(); + + /// <summary> + /// The comparer object supplied at creation time for this sorted collection. + /// </summary> + /// <value>The comparer</value> + SCG.IComparer<T> Comparer { get; } + + /// <summary> + /// Find the strict predecessor of item in the sorted collection, + /// that is, the greatest item in the collection smaller than the item. + /// </summary> + /// <param name="item">The item to find the predecessor for.</param> + /// <param name="res">The predecessor, if any; otherwise the default value for T.</param> + /// <returns>True if item has a predecessor; otherwise false.</returns> + bool TryPredecessor(T item, out T res); + + + /// <summary> + /// Find the strict successor of item in the sorted collection, + /// that is, the least item in the collection greater than the supplied value. + /// </summary> + /// <param name="item">The item to find the successor for.</param> + /// <param name="res">The successor, if any; otherwise the default value for T.</param> + /// <returns>True if item has a successor; otherwise false.</returns> + bool TrySuccessor(T item, out T res); + + + /// <summary> + /// Find the weak predecessor of item in the sorted collection, + /// that is, the greatest item in the collection smaller than or equal to the item. + /// </summary> + /// <param name="item">The item to find the weak predecessor for.</param> + /// <param name="res">The weak predecessor, if any; otherwise the default value for T.</param> + /// <returns>True if item has a weak predecessor; otherwise false.</returns> + bool TryWeakPredecessor(T item, out T res); + + + /// <summary> + /// Find the weak successor of item in the sorted collection, + /// that is, the least item in the collection greater than or equal to the supplied value. + /// </summary> + /// <param name="item">The item to find the weak successor for.</param> + /// <param name="res">The weak successor, if any; otherwise the default value for T.</param> + /// <returns>True if item has a weak successor; otherwise false.</returns> + bool TryWeakSuccessor(T item, out T res); + + + /// <summary> + /// Find the strict predecessor in the sorted collection of a particular value, + /// that is, the largest item in the collection less than the supplied value. + /// </summary> + /// <exception cref="NoSuchItemException"> if no such element exists (the + /// supplied value is less than or equal to the minimum of this collection.)</exception> + /// <param name="item">The item to find the predecessor for.</param> + /// <returns>The predecessor.</returns> + T Predecessor(T item); + + + /// <summary> + /// Find the strict successor in the sorted collection of a particular value, + /// that is, the least item in the collection greater than the supplied value. + /// </summary> + /// <exception cref="NoSuchItemException"> if no such element exists (the + /// supplied value is greater than or equal to the maximum of this collection.)</exception> + /// <param name="item">The item to find the successor for.</param> + /// <returns>The successor.</returns> + T Successor(T item); + + + /// <summary> + /// Find the weak predecessor in the sorted collection of a particular value, + /// that is, the largest item in the collection less than or equal to the supplied value. + /// </summary> + /// <exception cref="NoSuchItemException"> if no such element exists (the + /// supplied value is less than the minimum of this collection.)</exception> + /// <param name="item">The item to find the weak predecessor for.</param> + /// <returns>The weak predecessor.</returns> + T WeakPredecessor(T item); + + + /// <summary> + /// Find the weak successor in the sorted collection of a particular value, + /// that is, the least item in the collection greater than or equal to the supplied value. + /// </summary> + /// <exception cref="NoSuchItemException"> if no such element exists (the + /// supplied value is greater than the maximum of this collection.)</exception> + ///<param name="item">The item to find the weak successor for.</param> + /// <returns>The weak successor.</returns> + T WeakSuccessor(T item); + + + /// <summary> + /// Given a "cut" function from the items of the sorted collection to <code>int</code> + /// whose only sign changes when going through items in increasing order + /// can be + /// <list> + /// <item>from positive to zero</item> + /// <item>from positive to negative</item> + /// <item>from zero to negative</item> + /// </list> + /// The "cut" function is supplied as the <code>CompareTo</code> method + /// of an object <code>c</code> implementing + /// <code>IComparable<T></code>. + /// A typical example is the case where <code>T</code> is comparable and + /// <code>cutFunction</code> is itself of type <code>T</code>. + /// <para>This method performs a search in the sorted collection for the ranges in which the + /// "cut" function is negative, zero respectively positive. If <code>T</code> is comparable + /// and <code>c</code> is of type <code>T</code>, this is a safe way (no exceptions thrown) + /// to find predecessor and successor of <code>c</code>. + /// </para> + /// <para> If the supplied cut function does not satisfy the sign-change condition, + /// the result of this call is undefined. + /// </para> + /// + /// </summary> + /// <param name="cutFunction">The cut function <code>T</code> to <code>int</code>, given + /// by the <code>CompareTo</code> method of an object implementing + /// <code>IComparable<T></code>.</param> + /// <param name="low">Returns the largest item in the collection, where the + /// cut function is positive (if any).</param> + /// <param name="lowIsValid">Returns true if the cut function is positive somewhere + /// on this collection.</param> + /// <param name="high">Returns the least item in the collection, where the + /// cut function is negative (if any).</param> + /// <param name="highIsValid">Returns true if the cut function is negative somewhere + /// on this collection.</param> + /// <returns>True if the cut function is zero somewhere + /// on this collection.</returns> + bool Cut(IComparable<T> cutFunction, out T low, out bool lowIsValid, out T high, out bool highIsValid); + + + /// <summary> + /// Query this sorted collection for items greater than or equal to a supplied value. + /// <para>The returned collection is not a copy but a view into the collection.</para> + /// <para>The view is fragile in the sense that changes to the underlying collection will + /// invalidate the view so that further operations on the view throws InvalidView exceptions.</para> + /// </summary> + /// <param name="bot">The lower bound (inclusive).</param> + /// <returns>The result directed collection.</returns> + IDirectedEnumerable<T> RangeFrom(T bot); + + + /// <summary> + /// Query this sorted collection for items between two supplied values. + /// <para>The returned collection is not a copy but a view into the collection.</para> + /// <para>The view is fragile in the sense that changes to the underlying collection will + /// invalidate the view so that further operations on the view throws InvalidView exceptions.</para> + /// </summary> + /// <param name="bot">The lower bound (inclusive).</param> + /// <param name="top">The upper bound (exclusive).</param> + /// <returns>The result directed collection.</returns> + IDirectedEnumerable<T> RangeFromTo(T bot, T top); + + + /// <summary> + /// Query this sorted collection for items less than a supplied value. + /// <para>The returned collection is not a copy but a view into the collection.</para> + /// <para>The view is fragile in the sense that changes to the underlying collection will + /// invalidate the view so that further operations on the view throws InvalidView exceptions.</para> + /// </summary> + /// <param name="top">The upper bound (exclusive).</param> + /// <returns>The result directed collection.</returns> + IDirectedEnumerable<T> RangeTo(T top); + + + /// <summary> + /// Create a directed collection with the same items as this collection. + /// <para>The returned collection is not a copy but a view into the collection.</para> + /// <para>The view is fragile in the sense that changes to the underlying collection will + /// invalidate the view so that further operations on the view throws InvalidView exceptions.</para> + /// </summary> + /// <returns>The result directed collection.</returns> + IDirectedCollectionValue<T> RangeAll(); + + + //TODO: remove now that we assume that we can check the sorting order? + /// <summary> + /// Add all the items from another collection with an enumeration order that + /// is increasing in the items. + /// </summary> + /// <exception cref="ArgumentException"> if the enumerated items turns out + /// not to be in increasing order.</exception> + /// <param name="items">The collection to add.</param> + /// <typeparam name="U"></typeparam> + void AddSorted<U>(SCG.IEnumerable<U> items) where U : T; + + + /// <summary> + /// Remove all items of this collection above or at a supplied threshold. + /// </summary> + /// <param name="low">The lower threshold (inclusive).</param> + void RemoveRangeFrom(T low); + + + /// <summary> + /// Remove all items of this collection between two supplied thresholds. + /// </summary> + /// <param name="low">The lower threshold (inclusive).</param> + /// <param name="hi">The upper threshold (exclusive).</param> + void RemoveRangeFromTo(T low, T hi); + + + /// <summary> + /// Remove all items of this collection below a supplied threshold. + /// </summary> + /// <param name="hi">The upper threshold (exclusive).</param> + void RemoveRangeTo(T hi); + } + + + + /// <summary> + /// A collection where items are maintained in sorted order together + /// with their indexes in that order. + /// </summary> + public interface IIndexedSorted<T> : ISorted<T>, IIndexed<T> + { + /// <summary> + /// Determine the number of items at or above a supplied threshold. + /// </summary> + /// <param name="bot">The lower bound (inclusive)</param> + /// <returns>The number of matcing items.</returns> + int CountFrom(T bot); + + + /// <summary> + /// Determine the number of items between two supplied thresholds. + /// </summary> + /// <param name="bot">The lower bound (inclusive)</param> + /// <param name="top">The upper bound (exclusive)</param> + /// <returns>The number of matcing items.</returns> + int CountFromTo(T bot, T top); + + + /// <summary> + /// Determine the number of items below a supplied threshold. + /// </summary> + /// <param name="top">The upper bound (exclusive)</param> + /// <returns>The number of matcing items.</returns> + int CountTo(T top); + + + /// <summary> + /// Query this sorted collection for items greater than or equal to a supplied value. + /// </summary> + /// <param name="bot">The lower bound (inclusive).</param> + /// <returns>The result directed collection.</returns> + new IDirectedCollectionValue<T> RangeFrom(T bot); + + + /// <summary> + /// Query this sorted collection for items between two supplied values. + /// </summary> + /// <param name="bot">The lower bound (inclusive).</param> + /// <param name="top">The upper bound (exclusive).</param> + /// <returns>The result directed collection.</returns> + new IDirectedCollectionValue<T> RangeFromTo(T bot, T top); + + + /// <summary> + /// Query this sorted collection for items less than a supplied value. + /// </summary> + /// <param name="top">The upper bound (exclusive).</param> + /// <returns>The result directed collection.</returns> + new IDirectedCollectionValue<T> RangeTo(T top); + + + /// <summary> + /// Create a new indexed sorted collection consisting of the items of this + /// indexed sorted collection satisfying a certain predicate. + /// </summary> + /// <param name="predicate">The filter delegate defining the predicate.</param> + /// <returns>The new indexed sorted collection.</returns> + IIndexedSorted<T> FindAll(Fun<T, bool> predicate); + + + /// <summary> + /// Create a new indexed sorted collection consisting of the results of + /// mapping all items of this list. + /// <exception cref="ArgumentException"/> if the map is not increasing over + /// the items of this collection (with respect to the two given comparison + /// relations). + /// </summary> + /// <param name="mapper">The delegate definging the map.</param> + /// <param name="comparer">The comparion relation to use for the result.</param> + /// <returns>The new sorted collection.</returns> + IIndexedSorted<V> Map<V>(Fun<T, V> mapper, SCG.IComparer<V> comparer); + } + + + + /// <summary> + /// The type of a sorted collection with persistence + /// </summary> + public interface IPersistentSorted<T> : ISorted<T>, IDisposable + { + /// <summary> + /// Make a (read-only) snap shot of this collection. + /// </summary> + /// <returns>The snap shot.</returns> + ISorted<T> Snapshot(); + } + + + + /*************************************************************************/ + /// <summary> + /// A dictionary with keys of type K and values of type V. Equivalent to a + /// finite partial map from K to V. + /// </summary> + public interface IDictionary<K, V> : ICollectionValue<KeyValuePair<K, V>>, ICloneable + { + /// <summary> + /// The key equalityComparer. + /// </summary> + /// <value></value> + SCG.IEqualityComparer<K> EqualityComparer { get;} + + /// <summary> + /// Indexer for dictionary. + /// </summary> + /// <exception cref="NoSuchItemException"> if no entry is found. </exception> + /// <value>The value corresponding to the key</value> + V this[K key] { get; set;} + + + /// <summary> + /// + /// </summary> + /// <value>True if dictionary is read-only</value> + bool IsReadOnly { get;} + + + /// <summary> + /// + /// </summary> + /// <value>A collection containg the all the keys of the dictionary</value> + ICollectionValue<K> Keys { get;} + + + /// <summary> + /// + /// </summary> + /// <value>A collection containing all the values of the dictionary</value> + ICollectionValue<V> Values { get;} + + /// <summary> + /// + /// </summary> + /// <value>A delegate of type <see cref="T:C5.Fun`2"/> defining the partial function from K to V give by the dictionary.</value> + Fun<K, V> Fun { get; } + + + //TODO: resolve inconsistency: Add thows exception if key already there, AddAll ignores keys already There? + /// <summary> + /// Add a new (key, value) pair (a mapping) to the dictionary. + /// </summary> + /// <exception cref="DuplicateNotAllowedException"> if there already is an entry with the same key. </exception>> + /// <param name="key">Key to add</param> + /// <param name="val">Value to add</param> + void Add(K key, V val); + + /// <summary> + /// Add the entries from a collection of <see cref="T:C5.KeyValuePair`2"/> pairs to this dictionary. + /// </summary> + /// <exception cref="DuplicateNotAllowedException"> + /// If the input contains duplicate keys or a key already present in this dictionary.</exception> + /// <param name="entries"></param> + void AddAll<U, W>(SCG.IEnumerable<KeyValuePair<U, W>> entries) + where U : K + where W : V + ; + + /// <summary> + /// The value is symbolic indicating the type of asymptotic complexity + /// in terms of the size of this collection (worst-case or amortized as + /// relevant). + /// <para>See <see cref="T:C5.Speed"/> for the set of symbols.</para> + /// </summary> + /// <value>A characterization of the speed of lookup operations + /// (<code>Contains()</code> etc.) of the implementation of this dictionary.</value> + Speed ContainsSpeed { get;} + + /// <summary> + /// Check whether this collection contains all the values in another collection. + /// If this collection has bag semantics (<code>AllowsDuplicates==true</code>) + /// the check is made with respect to multiplicities, else multiplicities + /// are not taken into account. + /// </summary> + /// <param name="items">The </param> + /// <returns>True if all values in <code>items</code>is in this collection.</returns> + bool ContainsAll<H>(SCG.IEnumerable<H> items) where H : K; + + /// <summary> + /// Remove an entry with a given key from the dictionary + /// </summary> + /// <param name="key">The key of the entry to remove</param> + /// <returns>True if an entry was found (and removed)</returns> + bool Remove(K key); + + + /// <summary> + /// Remove an entry with a given key from the dictionary and report its value. + /// </summary> + /// <param name="key">The key of the entry to remove</param> + /// <param name="val">On exit, the value of the removed entry</param> + /// <returns>True if an entry was found (and removed)</returns> + bool Remove(K key, out V val); + + + /// <summary> + /// Remove all entries from the dictionary + /// </summary> + void Clear(); + + + /// <summary> + /// Check if there is an entry with a specified key + /// </summary> + /// <param name="key">The key to look for</param> + /// <returns>True if key was found</returns> + bool Contains(K key); + + + /// <summary> + /// Check if there is an entry with a specified key and report the corresponding + /// value if found. This can be seen as a safe form of "val = this[key]". + /// </summary> + /// <param name="key">The key to look for</param> + /// <param name="val">On exit, the value of the entry</param> + /// <returns>True if key was found</returns> + bool Find(K key, out V val); + + /// <summary> + /// Check if there is an entry with a specified key and report the corresponding + /// value if found. This can be seen as a safe form of "val = this[key]". + /// </summary> + /// <param name="key">The key to look for</param> + /// <param name="val">On exit, the value of the entry</param> + /// <returns>True if key was found</returns> + bool Find(ref K key, out V val); + + + /// <summary> + /// Look for a specific key in the dictionary and if found replace the value with a new one. + /// This can be seen as a non-adding version of "this[key] = val". + /// </summary> + /// <param name="key">The key to look for</param> + /// <param name="val">The new value</param> + /// <returns>True if key was found</returns> + bool Update(K key, V val); //no-adding + + + /// <summary> + /// Look for a specific key in the dictionary and if found replace the value with a new one. + /// This can be seen as a non-adding version of "this[key] = val" reporting the old value. + /// </summary> + /// <param name="key">The key to look for</param> + /// <param name="val">The new value</param> + /// <param name="oldval">The old value if any</param> + /// <returns>True if key was found</returns> + bool Update(K key, V val, out V oldval); //no-adding + + /// <summary> + /// Look for a specific key in the dictionary. If found, report the corresponding value, + /// else add an entry with the key and the supplied value. + /// </summary> + /// <param name="key">The key to look for</param> + /// <param name="val">On entry the value to add if the key is not found. + /// On exit the value found if any.</param> + /// <returns>True if key was found</returns> + bool FindOrAdd(K key, ref V val); //mixture + + + /// <summary> + /// Update value in dictionary corresponding to key if found, else add new entry. + /// More general than "this[key] = val;" by reporting if key was found. + /// </summary> + /// <param name="key">The key to look for</param> + /// <param name="val">The value to add or replace with.</param> + /// <returns>True if key was found and value updated.</returns> + bool UpdateOrAdd(K key, V val); + + + /// <summary> + /// Update value in dictionary corresponding to key if found, else add new entry. + /// More general than "this[key] = val;" by reporting if key was found. + /// </summary> + /// <param name="key">The key to look for</param> + /// <param name="val">The value to add or replace with.</param> + /// <param name="oldval">The old value if any</param> + /// <returns>True if key was found and value updated.</returns> + bool UpdateOrAdd(K key, V val, out V oldval); + + + /// <summary> + /// Check the integrity of the internal data structures of this dictionary. + /// Only avaliable in DEBUG builds??? + /// </summary> + /// <returns>True if check does not fail.</returns> + bool Check(); + } + + + + /// <summary> + /// A dictionary with sorted keys. + /// </summary> + public interface ISortedDictionary<K, V> : IDictionary<K, V> + { + /// <summary> + /// + /// </summary> + /// <value></value> + new ISorted<K> Keys { get;} + + /// <summary> + /// Find the current least item of this sorted collection. + /// </summary> + /// <exception cref="NoSuchItemException"> if the collection is empty.</exception> + /// <returns>The least item.</returns> + KeyValuePair<K, V> FindMin(); + + + /// <summary> + /// Remove the least item from this sorted collection. + /// </summary> + /// <exception cref="NoSuchItemException"> if the collection is empty.</exception> + /// <returns>The removed item.</returns> + KeyValuePair<K, V> DeleteMin(); + + + /// <summary> + /// Find the current largest item of this sorted collection. + /// </summary> + /// <exception cref="NoSuchItemException"> if the collection is empty.</exception> + /// <returns>The largest item.</returns> + KeyValuePair<K, V> FindMax(); + + + /// <summary> + /// Remove the largest item from this sorted collection. + /// </summary> + /// <exception cref="NoSuchItemException"> if the collection is empty.</exception> + /// <returns>The removed item.</returns> + KeyValuePair<K, V> DeleteMax(); + + /// <summary> + /// The key comparer used by this dictionary. + /// </summary> + /// <value></value> + SCG.IComparer<K> Comparer { get;} + + /// <summary> + /// Find the entry in the dictionary whose key is the + /// predecessor of the specified key. + /// </summary> + /// <param name="key">The key</param> + /// <param name="res">The predecessor, if any</param> + /// <returns>True if key has a predecessor</returns> + bool TryPredecessor(K key, out KeyValuePair<K, V> res); + + /// <summary> + /// Find the entry in the dictionary whose key is the + /// successor of the specified key. + /// </summary> + /// <param name="key">The key</param> + /// <param name="res">The successor, if any</param> + /// <returns>True if the key has a successor</returns> + bool TrySuccessor(K key, out KeyValuePair<K, V> res); + + /// <summary> + /// Find the entry in the dictionary whose key is the + /// weak predecessor of the specified key. + /// </summary> + /// <param name="key">The key</param> + /// <param name="res">The predecessor, if any</param> + /// <returns>True if key has a weak predecessor</returns> + bool TryWeakPredecessor(K key, out KeyValuePair<K, V> res); + + /// <summary> + /// Find the entry in the dictionary whose key is the + /// weak successor of the specified key. + /// </summary> + /// <param name="key">The key</param> + /// <param name="res">The weak successor, if any</param> + /// <returns>True if the key has a weak successor</returns> + bool TryWeakSuccessor(K key, out KeyValuePair<K, V> res); + + /// <summary> + /// Find the entry with the largest key less than a given key. + /// </summary> + /// <exception cref="NoSuchItemException"> if there is no such entry. </exception> + /// <param name="key">The key to compare to</param> + /// <returns>The entry</returns> + KeyValuePair<K, V> Predecessor(K key); + + + /// <summary> + /// Find the entry with the least key greater than a given key. + /// </summary> + /// <exception cref="NoSuchItemException"> if there is no such entry. </exception> + /// <param name="key">The key to compare to</param> + /// <returns>The entry</returns> + KeyValuePair<K, V> Successor(K key); + + + /// <summary> + /// Find the entry with the largest key less than or equal to a given key. + /// </summary> + /// <exception cref="NoSuchItemException"> if there is no such entry. </exception> + /// <param name="key">The key to compare to</param> + /// <returns>The entry</returns> + KeyValuePair<K, V> WeakPredecessor(K key); + + + /// <summary> + /// Find the entry with the least key greater than or equal to a given key. + /// </summary> + /// <exception cref="NoSuchItemException"> if there is no such entry. </exception> + /// <param name="key">The key to compare to</param> + /// <returns>The entry</returns> + KeyValuePair<K, V> WeakSuccessor(K key); + + /// <summary> + /// Given a "cut" function from the items of the sorted collection to <code>int</code> + /// whose only sign changes when going through items in increasing order + /// can be + /// <list> + /// <item>from positive to zero</item> + /// <item>from positive to negative</item> + /// <item>from zero to negative</item> + /// </list> + /// The "cut" function is supplied as the <code>CompareTo</code> method + /// of an object <code>c</code> implementing + /// <code>IComparable<K></code>. + /// A typical example is the case where <code>K</code> is comparable and + /// <code>c</code> is itself of type <code>K</code>. + /// <para>This method performs a search in the sorted collection for the ranges in which the + /// "cut" function is negative, zero respectively positive. If <code>K</code> is comparable + /// and <code>c</code> is of type <code>K</code>, this is a safe way (no exceptions thrown) + /// to find predecessor and successor of <code>c</code>. + /// </para> + /// <para> If the supplied cut function does not satisfy the sign-change condition, + /// the result of this call is undefined. + /// </para> + /// + /// </summary> + /// <param name="cutFunction">The cut function <code>K</code> to <code>int</code>, given + /// by the <code>CompareTo</code> method of an object implementing + /// <code>IComparable<K></code>.</param> + /// <param name="lowEntry">Returns the largest item in the collection, where the + /// cut function is positive (if any).</param> + /// <param name="lowIsValid">Returns true if the cut function is positive somewhere + /// on this collection.</param> + /// <param name="highEntry">Returns the least item in the collection, where the + /// cut function is negative (if any).</param> + /// <param name="highIsValid">Returns true if the cut function is negative somewhere + /// on this collection.</param> + /// <returns>True if the cut function is zero somewhere + /// on this collection.</returns> + bool Cut(IComparable<K> cutFunction, out KeyValuePair<K, V> lowEntry, out bool lowIsValid, out KeyValuePair<K, V> highEntry, out bool highIsValid); + + /// <summary> + /// Query this sorted collection for items greater than or equal to a supplied value. + /// <para>The returned collection is not a copy but a view into the collection.</para> + /// <para>The view is fragile in the sense that changes to the underlying collection will + /// invalidate the view so that further operations on the view throws InvalidView exceptions.</para> + /// </summary> + /// <param name="bot">The lower bound (inclusive).</param> + /// <returns>The result directed collection.</returns> + IDirectedEnumerable<KeyValuePair<K, V>> RangeFrom(K bot); + + + /// <summary> + /// Query this sorted collection for items between two supplied values. + /// <para>The returned collection is not a copy but a view into the collection.</para> + /// <para>The view is fragile in the sense that changes to the underlying collection will + /// invalidate the view so that further operations on the view throws InvalidView exceptions.</para> + /// </summary> + /// <param name="lowerBound">The lower bound (inclusive).</param> + /// <param name="upperBound">The upper bound (exclusive).</param> + /// <returns>The result directed collection.</returns> + IDirectedEnumerable<KeyValuePair<K, V>> RangeFromTo(K lowerBound, K upperBound); + + + /// <summary> + /// Query this sorted collection for items less than a supplied value. + /// <para>The returned collection is not a copy but a view into the collection.</para> + /// <para>The view is fragile in the sense that changes to the underlying collection will + /// invalidate the view so that further operations on the view throws InvalidView exceptions.</para> + /// </summary> + /// <param name="top">The upper bound (exclusive).</param> + /// <returns>The result directed collection.</returns> + IDirectedEnumerable<KeyValuePair<K, V>> RangeTo(K top); + + + /// <summary> + /// Create a directed collection with the same items as this collection. + /// <para>The returned collection is not a copy but a view into the collection.</para> + /// <para>The view is fragile in the sense that changes to the underlying collection will + /// invalidate the view so that further operations on the view throws InvalidView exceptions.</para> + /// </summary> + /// <returns>The result directed collection.</returns> + IDirectedCollectionValue<KeyValuePair<K, V>> RangeAll(); + + + //TODO: remove now that we assume that we can check the sorting order? + /// <summary> + /// Add all the items from another collection with an enumeration order that + /// is increasing in the items. + /// </summary> + /// <exception cref="ArgumentException"> if the enumerated items turns out + /// not to be in increasing order.</exception> + /// <param name="items">The collection to add.</param> + void AddSorted(SCG.IEnumerable<KeyValuePair<K, V>> items); + + + /// <summary> + /// Remove all items of this collection above or at a supplied threshold. + /// </summary> + /// <param name="low">The lower threshold (inclusive).</param> + void RemoveRangeFrom(K low); + + + /// <summary> + /// Remove all items of this collection between two supplied thresholds. + /// </summary> + /// <param name="low">The lower threshold (inclusive).</param> + /// <param name="hi">The upper threshold (exclusive).</param> + void RemoveRangeFromTo(K low, K hi); + + + /// <summary> + /// Remove all items of this collection below a supplied threshold. + /// </summary> + /// <param name="hi">The upper threshold (exclusive).</param> + void RemoveRangeTo(K hi); + } + + + + /*******************************************************************/ + /*/// <summary> + /// The type of an item comparer + /// <i>Implementations of this interface must asure that the method is self-consistent + /// and defines a sorting order on items, or state precise conditions under which this is true.</i> + /// <i>Implementations <b>must</b> assure that repeated calls of + /// the method to the same (in reference or binary identity sense) arguments + /// will return values with the same sign (-1, 0 or +1), or state precise conditions + /// under which the user + /// can be assured repeated calls will return the same sign.</i> + /// <i>Implementations of this interface must always return values from the method + /// and never throw exceptions.</i> + /// <i>This interface is identical to System.Collections.Generic.IComparer<T></i> + /// </summary> + public interface IComparer<T> + { + /// <summary> + /// Compare two items with respect to this item comparer + /// </summary> + /// <param name="item1">First item</param> + /// <param name="item2">Second item</param> + /// <returns>Positive if item1 is greater than item2, 0 if they are equal, negative if item1 is less than item2</returns> + int Compare(T item1, T item2); + } + + /// <summary> + /// The type of an item equalityComparer. + /// <i>Implementations of this interface <b>must</b> assure that the methods are + /// consistent, that is, that whenever two items i1 and i2 satisfies that Equals(i1,i2) + /// returns true, then GetHashCode returns the same value for i1 and i2.</i> + /// <i>Implementations of this interface <b>must</b> assure that repeated calls of + /// the methods to the same (in reference or binary identity sense) arguments + /// will return the same values, or state precise conditions under which the user + /// can be assured repeated calls will return the same values.</i> + /// <i>Implementations of this interface must always return values from the methods + /// and never throw exceptions.</i> + /// <i>This interface is similar in function to System.IKeyComparer<T></i> + /// </summary> + public interface SCG.IEqualityComparer<T> + { + /// <summary> + /// Get the hash code with respect to this item equalityComparer + /// </summary> + /// <param name="item">The item</param> + /// <returns>The hash code</returns> + int GetHashCode(T item); + + + /// <summary> + /// Check if two items are equal with respect to this item equalityComparer + /// </summary> + /// <param name="item1">first item</param> + /// <param name="item2">second item</param> + /// <returns>True if equal</returns> + bool Equals(T item1, T item2); + }*/ +} diff --git a/mcs/class/Mono.C5/C5/MappedEnumerators.cs b/mcs/class/Mono.C5/C5/MappedEnumerators.cs index 580a34d0d1e..f77cac9d05c 100644 --- a/mcs/class/Mono.C5/C5/MappedEnumerators.cs +++ b/mcs/class/Mono.C5/C5/MappedEnumerators.cs @@ -1,145 +1,145 @@ -/*
- Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-
-using System;
-using System.Diagnostics;
-using SCG = System.Collections.Generic;
-namespace C5
-{
- abstract class MappedDirectedCollectionValue<T, V> : DirectedCollectionValueBase<V>, IDirectedCollectionValue<V>
- {
- IDirectedCollectionValue<T> directedcollectionvalue;
-
- abstract public V Map(T item);
-
- public MappedDirectedCollectionValue(IDirectedCollectionValue<T> directedcollectionvalue)
- {
- this.directedcollectionvalue = directedcollectionvalue;
- }
-
- public override V Choose() { return Map(directedcollectionvalue.Choose()); }
-
- public override bool IsEmpty { get { return directedcollectionvalue.IsEmpty; } }
-
- public override int Count { get { return directedcollectionvalue.Count; } }
-
- public override Speed CountSpeed { get { return directedcollectionvalue.CountSpeed; } }
-
- public override IDirectedCollectionValue<V> Backwards()
- {
- MappedDirectedCollectionValue<T, V> retval = (MappedDirectedCollectionValue<T, V>)MemberwiseClone();
- retval.directedcollectionvalue = directedcollectionvalue.Backwards();
- return retval;
- //If we made this classs non-abstract we could do
- //return new MappedDirectedCollectionValue<T,V>(directedcollectionvalue.Backwards());;
- }
-
-
- public override SCG.IEnumerator<V> GetEnumerator()
- {
- foreach (T item in directedcollectionvalue)
- yield return Map(item);
- }
-
- public override EnumerationDirection Direction
- {
- get { return directedcollectionvalue.Direction; }
- }
-
- IDirectedEnumerable<V> IDirectedEnumerable<V>.Backwards()
- {
- return Backwards();
- }
-
-
- }
-
- abstract class MappedCollectionValue<T, V> : CollectionValueBase<V>, ICollectionValue<V>
- {
- ICollectionValue<T> collectionvalue;
-
- abstract public V Map(T item);
-
- public MappedCollectionValue(ICollectionValue<T> collectionvalue)
- {
- this.collectionvalue = collectionvalue;
- }
-
- public override V Choose() { return Map(collectionvalue.Choose()); }
-
- public override bool IsEmpty { get { return collectionvalue.IsEmpty; } }
-
- public override int Count { get { return collectionvalue.Count; } }
-
- public override Speed CountSpeed { get { return collectionvalue.CountSpeed; } }
-
- public override SCG.IEnumerator<V> GetEnumerator()
- {
- foreach (T item in collectionvalue)
- yield return Map(item);
- }
- }
-
- class MultiplicityOne<K> : MappedCollectionValue<K, KeyValuePair<K, int>>
- {
- public MultiplicityOne(ICollectionValue<K> coll):base(coll) { }
- public override KeyValuePair<K, int> Map(K k) { return new KeyValuePair<K, int>(k, 1); }
- }
-
- class DropMultiplicity<K> : MappedCollectionValue<KeyValuePair<K, int>, K>
- {
- public DropMultiplicity(ICollectionValue<KeyValuePair<K, int>> coll):base(coll) { }
- public override K Map(KeyValuePair<K, int> kvp) { return kvp.Key; }
- }
-
- abstract class MappedDirectedEnumerable<T, V> : EnumerableBase<V>, IDirectedEnumerable<V>
- {
- IDirectedEnumerable<T> directedenumerable;
-
- abstract public V Map(T item);
-
- public MappedDirectedEnumerable(IDirectedEnumerable<T> directedenumerable)
- {
- this.directedenumerable = directedenumerable;
- }
-
- public IDirectedEnumerable<V> Backwards()
- {
- MappedDirectedEnumerable<T, V> retval = (MappedDirectedEnumerable<T, V>)MemberwiseClone();
- retval.directedenumerable = directedenumerable.Backwards();
- return retval;
- //If we made this classs non-abstract we could do
- //return new MappedDirectedCollectionValue<T,V>(directedcollectionvalue.Backwards());;
- }
-
-
- public override SCG.IEnumerator<V> GetEnumerator()
- {
- foreach (T item in directedenumerable)
- yield return Map(item);
- }
-
- public EnumerationDirection Direction
- {
- get { return directedenumerable.Direction; }
- }
- }
+/* + Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +using System; +using System.Diagnostics; +using SCG = System.Collections.Generic; +namespace C5 +{ + abstract class MappedDirectedCollectionValue<T, V> : DirectedCollectionValueBase<V>, IDirectedCollectionValue<V> + { + IDirectedCollectionValue<T> directedcollectionvalue; + + abstract public V Map(T item); + + public MappedDirectedCollectionValue(IDirectedCollectionValue<T> directedcollectionvalue) + { + this.directedcollectionvalue = directedcollectionvalue; + } + + public override V Choose() { return Map(directedcollectionvalue.Choose()); } + + public override bool IsEmpty { get { return directedcollectionvalue.IsEmpty; } } + + public override int Count { get { return directedcollectionvalue.Count; } } + + public override Speed CountSpeed { get { return directedcollectionvalue.CountSpeed; } } + + public override IDirectedCollectionValue<V> Backwards() + { + MappedDirectedCollectionValue<T, V> retval = (MappedDirectedCollectionValue<T, V>)MemberwiseClone(); + retval.directedcollectionvalue = directedcollectionvalue.Backwards(); + return retval; + //If we made this classs non-abstract we could do + //return new MappedDirectedCollectionValue<T,V>(directedcollectionvalue.Backwards());; + } + + + public override SCG.IEnumerator<V> GetEnumerator() + { + foreach (T item in directedcollectionvalue) + yield return Map(item); + } + + public override EnumerationDirection Direction + { + get { return directedcollectionvalue.Direction; } + } + + IDirectedEnumerable<V> IDirectedEnumerable<V>.Backwards() + { + return Backwards(); + } + + + } + + abstract class MappedCollectionValue<T, V> : CollectionValueBase<V>, ICollectionValue<V> + { + ICollectionValue<T> collectionvalue; + + abstract public V Map(T item); + + public MappedCollectionValue(ICollectionValue<T> collectionvalue) + { + this.collectionvalue = collectionvalue; + } + + public override V Choose() { return Map(collectionvalue.Choose()); } + + public override bool IsEmpty { get { return collectionvalue.IsEmpty; } } + + public override int Count { get { return collectionvalue.Count; } } + + public override Speed CountSpeed { get { return collectionvalue.CountSpeed; } } + + public override SCG.IEnumerator<V> GetEnumerator() + { + foreach (T item in collectionvalue) + yield return Map(item); + } + } + + class MultiplicityOne<K> : MappedCollectionValue<K, KeyValuePair<K, int>> + { + public MultiplicityOne(ICollectionValue<K> coll):base(coll) { } + public override KeyValuePair<K, int> Map(K k) { return new KeyValuePair<K, int>(k, 1); } + } + + class DropMultiplicity<K> : MappedCollectionValue<KeyValuePair<K, int>, K> + { + public DropMultiplicity(ICollectionValue<KeyValuePair<K, int>> coll):base(coll) { } + public override K Map(KeyValuePair<K, int> kvp) { return kvp.Key; } + } + + abstract class MappedDirectedEnumerable<T, V> : EnumerableBase<V>, IDirectedEnumerable<V> + { + IDirectedEnumerable<T> directedenumerable; + + abstract public V Map(T item); + + public MappedDirectedEnumerable(IDirectedEnumerable<T> directedenumerable) + { + this.directedenumerable = directedenumerable; + } + + public IDirectedEnumerable<V> Backwards() + { + MappedDirectedEnumerable<T, V> retval = (MappedDirectedEnumerable<T, V>)MemberwiseClone(); + retval.directedenumerable = directedenumerable.Backwards(); + return retval; + //If we made this classs non-abstract we could do + //return new MappedDirectedCollectionValue<T,V>(directedcollectionvalue.Backwards());; + } + + + public override SCG.IEnumerator<V> GetEnumerator() + { + foreach (T item in directedenumerable) + yield return Map(item); + } + + public EnumerationDirection Direction + { + get { return directedenumerable.Direction; } + } + } }
\ No newline at end of file diff --git a/mcs/class/Mono.C5/C5/Random.cs b/mcs/class/Mono.C5/C5/Random.cs index 823e9c72224..7e1623a7117 100644 --- a/mcs/class/Mono.C5/C5/Random.cs +++ b/mcs/class/Mono.C5/C5/Random.cs @@ -1,173 +1,173 @@ -/*
- Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-using System;
-using System.Diagnostics;
-using SCG = System.Collections.Generic;
-namespace C5
-{
- /// <summary>
- /// A modern random number generator based on G. Marsaglia:
- /// Seeds for Random Number Generators, Communications of the
- /// ACM 46, 5 (May 2003) 90-93; and a posting by Marsaglia to
- /// comp.lang.c on 2003-04-03.
- /// </summary>
- public class C5Random : Random
- {
- private uint[] Q = new uint[16];
-
- private uint c = 362436, i = 15;
-
-
- private uint Cmwc()
- {
- ulong t, a = 487198574UL;
- uint x, r = 0xfffffffe;
-
- i = (i + 1) & 15;
- t = a * Q[i] + c;
- c = (uint)(t >> 32);
- x = (uint)(t + c);
- if (x < c)
- {
- x++;
- c++;
- }
-
- return Q[i] = r - x;
- }
-
-
- /// <summary>
- /// Get a new random System.Double value
- /// </summary>
- /// <returns>The random double</returns>
- public override double NextDouble()
- {
- return Cmwc() / 4294967296.0;
- }
-
-
- /// <summary>
- /// Get a new random System.Double value
- /// </summary>
- /// <returns>The random double</returns>
- protected override double Sample()
- {
- return NextDouble();
- }
-
-
- /// <summary>
- /// Get a new random System.Int32 value
- /// </summary>
- /// <returns>The random int</returns>
- public override int Next()
- {
- return (int)Cmwc();
- }
-
-
- /// <summary>
- /// Get a random non-negative integer less than a given upper bound
- /// </summary>
- /// <exception cref="ArgumentException">If max is negative</exception>
- /// <param name="max">The upper bound (exclusive)</param>
- /// <returns></returns>
- public override int Next(int max)
- {
- if (max < 0)
- throw new ArgumentException("max must be non-negative");
-
- return (int)(Cmwc() / 4294967296.0 * max);
- }
-
-
- /// <summary>
- /// Get a random integer between two given bounds
- /// </summary>
- /// <exception cref="ArgumentException">If max is less than min</exception>
- /// <param name="min">The lower bound (inclusive)</param>
- /// <param name="max">The upper bound (exclusive)</param>
- /// <returns></returns>
- public override int Next(int min, int max)
- {
- if (min > max)
- throw new ArgumentException("min must be less than or equal to max");
-
- return min + (int)(Cmwc() / 4294967296.0 * (max - min));
- }
-
- /// <summary>
- /// Fill a array of byte with random bytes
- /// </summary>
- /// <param name="buffer">The array to fill</param>
- public override void NextBytes(byte[] buffer)
- {
- for (int i = 0, length = buffer.Length; i < length; i++)
- buffer[i] = (byte)Cmwc();
- }
-
-
- /// <summary>
- /// Create a random number generator seed by system time.
- /// </summary>
- public C5Random() : this(DateTime.Now.Ticks)
- {
- }
-
-
- /// <summary>
- /// Create a random number generator with a given seed
- /// </summary>
- /// <exception cref="ArgumentException">If seed is zero</exception>
- /// <param name="seed">The seed</param>
- public C5Random(long seed)
- {
- if (seed == 0)
- throw new ArgumentException("Seed must be non-zero");
-
- uint j = (uint)(seed & 0xFFFFFFFF);
-
- for (int i = 0; i < 16; i++)
- {
- j ^= j << 13;
- j ^= j >> 17;
- j ^= j << 5;
- Q[i] = j;
- }
-
- Q[15] = (uint)(seed ^ (seed >> 32));
- }
-
- /// <summary>
- /// Create a random number generator with a specified internal start state.
- /// </summary>
- /// <exception cref="ArgumentException">If Q is not of length exactly 16</exception>
- /// <param name="Q">The start state. Must be a collection of random bits given by an array of exactly 16 uints.</param>
- [CLSCompliant(false)]
- public C5Random(uint[] Q)
- {
- if (Q.Length != 16)
- throw new ArgumentException("Q must have length 16, was " + Q.Length);
- Array.Copy(Q, this.Q, 16);
- }
- }
+/* + Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +using System; +using System.Diagnostics; +using SCG = System.Collections.Generic; +namespace C5 +{ + /// <summary> + /// A modern random number generator based on G. Marsaglia: + /// Seeds for Random Number Generators, Communications of the + /// ACM 46, 5 (May 2003) 90-93; and a posting by Marsaglia to + /// comp.lang.c on 2003-04-03. + /// </summary> + public class C5Random : Random + { + private uint[] Q = new uint[16]; + + private uint c = 362436, i = 15; + + + private uint Cmwc() + { + ulong t, a = 487198574UL; + uint x, r = 0xfffffffe; + + i = (i + 1) & 15; + t = a * Q[i] + c; + c = (uint)(t >> 32); + x = (uint)(t + c); + if (x < c) + { + x++; + c++; + } + + return Q[i] = r - x; + } + + + /// <summary> + /// Get a new random System.Double value + /// </summary> + /// <returns>The random double</returns> + public override double NextDouble() + { + return Cmwc() / 4294967296.0; + } + + + /// <summary> + /// Get a new random System.Double value + /// </summary> + /// <returns>The random double</returns> + protected override double Sample() + { + return NextDouble(); + } + + + /// <summary> + /// Get a new random System.Int32 value + /// </summary> + /// <returns>The random int</returns> + public override int Next() + { + return (int)Cmwc(); + } + + + /// <summary> + /// Get a random non-negative integer less than a given upper bound + /// </summary> + /// <exception cref="ArgumentException">If max is negative</exception> + /// <param name="max">The upper bound (exclusive)</param> + /// <returns></returns> + public override int Next(int max) + { + if (max < 0) + throw new ArgumentException("max must be non-negative"); + + return (int)(Cmwc() / 4294967296.0 * max); + } + + + /// <summary> + /// Get a random integer between two given bounds + /// </summary> + /// <exception cref="ArgumentException">If max is less than min</exception> + /// <param name="min">The lower bound (inclusive)</param> + /// <param name="max">The upper bound (exclusive)</param> + /// <returns></returns> + public override int Next(int min, int max) + { + if (min > max) + throw new ArgumentException("min must be less than or equal to max"); + + return min + (int)(Cmwc() / 4294967296.0 * (max - min)); + } + + /// <summary> + /// Fill a array of byte with random bytes + /// </summary> + /// <param name="buffer">The array to fill</param> + public override void NextBytes(byte[] buffer) + { + for (int i = 0, length = buffer.Length; i < length; i++) + buffer[i] = (byte)Cmwc(); + } + + + /// <summary> + /// Create a random number generator seed by system time. + /// </summary> + public C5Random() : this(DateTime.Now.Ticks) + { + } + + + /// <summary> + /// Create a random number generator with a given seed + /// </summary> + /// <exception cref="ArgumentException">If seed is zero</exception> + /// <param name="seed">The seed</param> + public C5Random(long seed) + { + if (seed == 0) + throw new ArgumentException("Seed must be non-zero"); + + uint j = (uint)(seed & 0xFFFFFFFF); + + for (int i = 0; i < 16; i++) + { + j ^= j << 13; + j ^= j >> 17; + j ^= j << 5; + Q[i] = j; + } + + Q[15] = (uint)(seed ^ (seed >> 32)); + } + + /// <summary> + /// Create a random number generator with a specified internal start state. + /// </summary> + /// <exception cref="ArgumentException">If Q is not of length exactly 16</exception> + /// <param name="Q">The start state. Must be a collection of random bits given by an array of exactly 16 uints.</param> + [CLSCompliant(false)] + public C5Random(uint[] Q) + { + if (Q.Length != 16) + throw new ArgumentException("Q must have length 16, was " + Q.Length); + Array.Copy(Q, this.Q, 16); + } + } }
\ No newline at end of file diff --git a/mcs/class/Mono.C5/C5/Records.cs b/mcs/class/Mono.C5/C5/Records.cs index 47adaa9c070..ff30435b534 100644 --- a/mcs/class/Mono.C5/C5/Records.cs +++ b/mcs/class/Mono.C5/C5/Records.cs @@ -1,510 +1,510 @@ -/*
- Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-
-using C5;
-using System;
-using System.Reflection;
-using System.Reflection.Emit;
-using System.Diagnostics;
-
-namespace C5
-{
- struct RecConst
- {
- public const int HASHFACTOR = 387281;
- }
- /// <summary>
- /// A generic record type with two fields.
- /// <para>
- /// Equality is defined field by field, using the <code>Equals</code> method
- /// inherited from <code>System.Object</code> (i.e. using <see cref="T:C5.NaturalEqualityComparer`1"/>).
- /// </para>
- /// <para>
- /// This type is similar to <see cref="T:C5.KeyValuePair`2"/>, but the latter
- /// uses <see cref="P:C5.EqualityComparer`1.Default"/> to define field equality instead of <see cref="T:C5.NaturalEqualityComparer`1"/>.
- /// </para>
- /// </summary>
- /// <typeparam name="T1"></typeparam>
- /// <typeparam name="T2"></typeparam>
- public struct Rec<T1, T2> : IEquatable<Rec<T1, T2>>, IShowable
- {
- /// <summary>
- ///
- /// </summary>
- public readonly T1 X1;
- /// <summary>
- ///
- /// </summary>
- public readonly T2 X2;
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="x1"></param>
- /// <param name="x2"></param>
- [Tested]
- public Rec(T1 x1, T2 x2)
- {
- this.X1 = x1; this.X2 = x2;
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="other"></param>
- /// <returns></returns>
- [Tested]
- public bool Equals(Rec<T1, T2> other)
- {
- return
- (X1 == null ? other.X1 == null : X1.Equals(other.X1)) &&
- (X2 == null ? other.X2 == null : X2.Equals(other.X2))
- ;
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="obj"></param>
- /// <returns></returns>
- [Tested]
- public override bool Equals(object obj)
- {
- return obj is Rec<T1, T2> ? Equals((Rec<T1, T2>)obj) : false;
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="record1"></param>
- /// <param name="record2"></param>
- /// <returns></returns>
- [Tested]
- public static bool operator ==(Rec<T1, T2> record1, Rec<T1, T2> record2)
- {
- return record1.Equals(record2);
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="record1"></param>
- /// <param name="record2"></param>
- /// <returns></returns>
- [Tested]
- public static bool operator !=(Rec<T1, T2> record1, Rec<T1, T2> record2)
- {
- return !record1.Equals(record2);
- }
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- [Tested]
- public override int GetHashCode()
- {
- //TODO: don't use 0 as hashcode for null, but something else!
- int hashcode = X1 == null ? 0 : X1.GetHashCode();
- hashcode = hashcode * RecConst.HASHFACTOR + (X2 == null ? 0 : X2.GetHashCode());
- return hashcode;
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public override string ToString()
- {
- return String.Format("({0}, {1})", X1, X2);
- }
-
- #region IShowable Members
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="stringbuilder"></param>
- /// <param name="rest"></param>
- /// <param name="formatProvider"></param>
- /// <returns></returns>
- public bool Show(System.Text.StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider)
- {
- bool incomplete = true;
- stringbuilder.Append("(");
- rest -= 2;
- try
- {
- if (incomplete = !Showing.Show(X1, stringbuilder, ref rest, formatProvider))
- return false;
- stringbuilder.Append(", ");
- rest -= 2;
- if (incomplete = !Showing.Show(X2, stringbuilder, ref rest, formatProvider))
- return false;
- }
- finally
- {
- if (incomplete)
- {
- stringbuilder.Append("...");
- rest -= 3;
- }
- stringbuilder.Append(")");
- }
- return true;
- }
- #endregion
-
- #region IFormattable Members
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="format"></param>
- /// <param name="formatProvider"></param>
- /// <returns></returns>
- public string ToString(string format, IFormatProvider formatProvider)
- {
- return Showing.ShowString(this, format, formatProvider);
- }
-
- #endregion
- }
- /// <summary>
- ///
- /// </summary>
- /// <typeparam name="T1"></typeparam>
- /// <typeparam name="T2"></typeparam>
- /// <typeparam name="T3"></typeparam>
- public struct Rec<T1, T2, T3> : IEquatable<Rec<T1, T2, T3>>, IShowable
- {
- /// <summary>
- ///
- /// </summary>
- public readonly T1 X1;
- /// <summary>
- ///
- /// </summary>
- public readonly T2 X2;
- /// <summary>
- ///
- /// </summary>
- public readonly T3 X3;
- /// <summary>
- ///
- /// </summary>
- /// <param name="x1"></param>
- /// <param name="x2"></param>
- /// <param name="x3"></param>
- [Tested]
- public Rec(T1 x1, T2 x2, T3 x3)
- {
- this.X1 = x1; this.X2 = x2; this.X3 = x3;
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="other"></param>
- /// <returns></returns>
- [Tested]
- public bool Equals(Rec<T1, T2, T3> other)
- {
- return
- (X1 == null ? other.X1 == null : X1.Equals(other.X1)) &&
- (X2 == null ? other.X2 == null : X2.Equals(other.X2)) &&
- (X3 == null ? other.X3 == null : X3.Equals(other.X3))
- ;
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="obj"></param>
- /// <returns></returns>
- [Tested]
- public override bool Equals(object obj)
- {
- return obj is Rec<T1, T2, T3> ? Equals((Rec<T1, T2, T3>)obj) : false;
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="record1"></param>
- /// <param name="record2"></param>
- /// <returns></returns>
- [Tested]
- public static bool operator ==(Rec<T1, T2, T3> record1, Rec<T1, T2, T3> record2)
- {
- return record1.Equals(record2);
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="record1"></param>
- /// <param name="record2"></param>
- /// <returns></returns>
- [Tested]
- public static bool operator !=(Rec<T1, T2, T3> record1, Rec<T1, T2, T3> record2)
- {
- return !record1.Equals(record2);
- }
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- [Tested]
- public override int GetHashCode()
- {
- //TODO: don't use 0 as hashcode for null, but something else!
- int hashcode = X1 == null ? 0 : X1.GetHashCode();
- hashcode = hashcode * RecConst.HASHFACTOR + (X2 == null ? 0 : X2.GetHashCode());
- hashcode = hashcode * RecConst.HASHFACTOR + (X3 == null ? 0 : X3.GetHashCode());
- return hashcode;
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public override string ToString()
- {
- return String.Format("({0}, {1}, {2})", X1, X2, X3);
- }
- #region IShowable Members
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="stringbuilder"></param>
- /// <param name="rest"></param>
- /// <param name="formatProvider"></param>
- /// <returns></returns>
- public bool Show(System.Text.StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider)
- {
- bool incomplete = true;
- stringbuilder.Append("(");
- rest -= 2;
- try
- {
- if (incomplete = !Showing.Show(X1, stringbuilder, ref rest, formatProvider))
- return false;
- stringbuilder.Append(", ");
- rest -= 2;
- if (incomplete = !Showing.Show(X2, stringbuilder, ref rest, formatProvider))
- return false;
- stringbuilder.Append(", ");
- rest -= 2;
- if (incomplete = !Showing.Show(X3, stringbuilder, ref rest, formatProvider))
- return false;
- }
- finally
- {
- if (incomplete)
- {
- stringbuilder.Append("...");
- rest -= 3;
- }
- stringbuilder.Append(")");
- }
- return true;
- }
- #endregion
-
- #region IFormattable Members
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="format"></param>
- /// <param name="formatProvider"></param>
- /// <returns></returns>
- public string ToString(string format, IFormatProvider formatProvider)
- {
- return Showing.ShowString(this, format, formatProvider);
- }
-
- #endregion
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <typeparam name="T1"></typeparam>
- /// <typeparam name="T2"></typeparam>
- /// <typeparam name="T3"></typeparam>
- /// <typeparam name="T4"></typeparam>
- public struct Rec<T1, T2, T3, T4> : IEquatable<Rec<T1, T2, T3, T4>>, IShowable
- {
- /// <summary>
- ///
- /// </summary>
- public readonly T1 X1;
- /// <summary>
- ///
- /// </summary>
- public readonly T2 X2;
- /// <summary>
- ///
- /// </summary>
- public readonly T3 X3;
- /// <summary>
- ///
- /// </summary>
- public readonly T4 X4;
- /// <summary>
- ///
- /// </summary>
- /// <param name="x1"></param>
- /// <param name="x2"></param>
- /// <param name="x3"></param>
- /// <param name="x4"></param>
- [Tested]
- public Rec(T1 x1, T2 x2, T3 x3, T4 x4)
- {
- this.X1 = x1; this.X2 = x2; this.X3 = x3; this.X4 = x4;
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="other"></param>
- /// <returns></returns>
- [Tested]
- public bool Equals(Rec<T1, T2, T3, T4> other)
- {
- return
- (X1 == null ? other.X1 == null : X1.Equals(other.X1)) &&
- (X2 == null ? other.X2 == null : X2.Equals(other.X2)) &&
- (X3 == null ? other.X3 == null : X3.Equals(other.X3)) &&
- (X4 == null ? other.X4 == null : X4.Equals(other.X4))
- ;
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="obj"></param>
- /// <returns></returns>
- [Tested]
- public override bool Equals(object obj)
- {
- return obj is Rec<T1, T2, T3, T4> ? Equals((Rec<T1, T2, T3, T4>)obj) : false;
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="record1"></param>
- /// <param name="record2"></param>
- /// <returns></returns>
- [Tested]
- public static bool operator ==(Rec<T1, T2, T3, T4> record1, Rec<T1, T2, T3, T4> record2)
- {
- return record1.Equals(record2);
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="record1"></param>
- /// <param name="record2"></param>
- /// <returns></returns>
- [Tested]
- public static bool operator !=(Rec<T1, T2, T3, T4> record1, Rec<T1, T2, T3, T4> record2)
- {
- return !record1.Equals(record2);
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- [Tested]
- public override int GetHashCode()
- {
- //TODO: don't use 0 as hashcode for null, but something else!
- int hashcode = X1 == null ? 0 : X1.GetHashCode();
- hashcode = hashcode * RecConst.HASHFACTOR + (X2 == null ? 0 : X2.GetHashCode());
- hashcode = hashcode * RecConst.HASHFACTOR + (X3 == null ? 0 : X3.GetHashCode());
- hashcode = hashcode * RecConst.HASHFACTOR + (X4 == null ? 0 : X4.GetHashCode());
- return hashcode;
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public override string ToString()
- {
- return String.Format("({0}, {1}, {2}, {3})", X1, X2, X3, X4);
- }
- #region IShowable Members
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="stringbuilder"></param>
- /// <param name="rest"></param>
- /// <param name="formatProvider"></param>
- /// <returns></returns>
- public bool Show(System.Text.StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider)
- {
- bool incomplete = true;
- stringbuilder.Append("(");
- rest -= 2;
- try
- {
- if (incomplete = !Showing.Show(X1, stringbuilder, ref rest, formatProvider))
- return false;
- stringbuilder.Append(", ");
- rest -= 2;
- if (incomplete = !Showing.Show(X2, stringbuilder, ref rest, formatProvider))
- return false;
- stringbuilder.Append(", ");
- rest -= 2;
- if (incomplete = !Showing.Show(X3, stringbuilder, ref rest, formatProvider))
- return false;
- stringbuilder.Append(", ");
- rest -= 2;
- if (incomplete = !Showing.Show(X4, stringbuilder, ref rest, formatProvider))
- return false;
- }
- finally
- {
- if (incomplete)
- {
- stringbuilder.Append("...");
- rest -= 3;
- }
- stringbuilder.Append(")");
- }
- return true;
- }
- #endregion
-
- #region IFormattable Members
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="format"></param>
- /// <param name="formatProvider"></param>
- /// <returns></returns>
- public string ToString(string format, IFormatProvider formatProvider)
- {
- return Showing.ShowString(this, format, formatProvider);
- }
-
- #endregion
- }
-}
+/* + Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +using C5; +using System; +using System.Reflection; +using System.Reflection.Emit; +using System.Diagnostics; + +namespace C5 +{ + struct RecConst + { + public const int HASHFACTOR = 387281; + } + /// <summary> + /// A generic record type with two fields. + /// <para> + /// Equality is defined field by field, using the <code>Equals</code> method + /// inherited from <code>System.Object</code> (i.e. using <see cref="T:C5.NaturalEqualityComparer`1"/>). + /// </para> + /// <para> + /// This type is similar to <see cref="T:C5.KeyValuePair`2"/>, but the latter + /// uses <see cref="P:C5.EqualityComparer`1.Default"/> to define field equality instead of <see cref="T:C5.NaturalEqualityComparer`1"/>. + /// </para> + /// </summary> + /// <typeparam name="T1"></typeparam> + /// <typeparam name="T2"></typeparam> + public struct Rec<T1, T2> : IEquatable<Rec<T1, T2>>, IShowable + { + /// <summary> + /// + /// </summary> + public readonly T1 X1; + /// <summary> + /// + /// </summary> + public readonly T2 X2; + + /// <summary> + /// + /// </summary> + /// <param name="x1"></param> + /// <param name="x2"></param> + [Tested] + public Rec(T1 x1, T2 x2) + { + this.X1 = x1; this.X2 = x2; + } + + /// <summary> + /// + /// </summary> + /// <param name="other"></param> + /// <returns></returns> + [Tested] + public bool Equals(Rec<T1, T2> other) + { + return + (X1 == null ? other.X1 == null : X1.Equals(other.X1)) && + (X2 == null ? other.X2 == null : X2.Equals(other.X2)) + ; + } + /// <summary> + /// + /// </summary> + /// <param name="obj"></param> + /// <returns></returns> + [Tested] + public override bool Equals(object obj) + { + return obj is Rec<T1, T2> ? Equals((Rec<T1, T2>)obj) : false; + } + /// <summary> + /// + /// </summary> + /// <param name="record1"></param> + /// <param name="record2"></param> + /// <returns></returns> + [Tested] + public static bool operator ==(Rec<T1, T2> record1, Rec<T1, T2> record2) + { + return record1.Equals(record2); + } + /// <summary> + /// + /// </summary> + /// <param name="record1"></param> + /// <param name="record2"></param> + /// <returns></returns> + [Tested] + public static bool operator !=(Rec<T1, T2> record1, Rec<T1, T2> record2) + { + return !record1.Equals(record2); + } + /// <summary> + /// + /// </summary> + /// <returns></returns> + [Tested] + public override int GetHashCode() + { + //TODO: don't use 0 as hashcode for null, but something else! + int hashcode = X1 == null ? 0 : X1.GetHashCode(); + hashcode = hashcode * RecConst.HASHFACTOR + (X2 == null ? 0 : X2.GetHashCode()); + return hashcode; + } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public override string ToString() + { + return String.Format("({0}, {1})", X1, X2); + } + + #region IShowable Members + + /// <summary> + /// + /// </summary> + /// <param name="stringbuilder"></param> + /// <param name="rest"></param> + /// <param name="formatProvider"></param> + /// <returns></returns> + public bool Show(System.Text.StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider) + { + bool incomplete = true; + stringbuilder.Append("("); + rest -= 2; + try + { + if (incomplete = !Showing.Show(X1, stringbuilder, ref rest, formatProvider)) + return false; + stringbuilder.Append(", "); + rest -= 2; + if (incomplete = !Showing.Show(X2, stringbuilder, ref rest, formatProvider)) + return false; + } + finally + { + if (incomplete) + { + stringbuilder.Append("..."); + rest -= 3; + } + stringbuilder.Append(")"); + } + return true; + } + #endregion + + #region IFormattable Members + + /// <summary> + /// + /// </summary> + /// <param name="format"></param> + /// <param name="formatProvider"></param> + /// <returns></returns> + public string ToString(string format, IFormatProvider formatProvider) + { + return Showing.ShowString(this, format, formatProvider); + } + + #endregion + } + /// <summary> + /// + /// </summary> + /// <typeparam name="T1"></typeparam> + /// <typeparam name="T2"></typeparam> + /// <typeparam name="T3"></typeparam> + public struct Rec<T1, T2, T3> : IEquatable<Rec<T1, T2, T3>>, IShowable + { + /// <summary> + /// + /// </summary> + public readonly T1 X1; + /// <summary> + /// + /// </summary> + public readonly T2 X2; + /// <summary> + /// + /// </summary> + public readonly T3 X3; + /// <summary> + /// + /// </summary> + /// <param name="x1"></param> + /// <param name="x2"></param> + /// <param name="x3"></param> + [Tested] + public Rec(T1 x1, T2 x2, T3 x3) + { + this.X1 = x1; this.X2 = x2; this.X3 = x3; + } + /// <summary> + /// + /// </summary> + /// <param name="other"></param> + /// <returns></returns> + [Tested] + public bool Equals(Rec<T1, T2, T3> other) + { + return + (X1 == null ? other.X1 == null : X1.Equals(other.X1)) && + (X2 == null ? other.X2 == null : X2.Equals(other.X2)) && + (X3 == null ? other.X3 == null : X3.Equals(other.X3)) + ; + } + /// <summary> + /// + /// </summary> + /// <param name="obj"></param> + /// <returns></returns> + [Tested] + public override bool Equals(object obj) + { + return obj is Rec<T1, T2, T3> ? Equals((Rec<T1, T2, T3>)obj) : false; + } + /// <summary> + /// + /// </summary> + /// <param name="record1"></param> + /// <param name="record2"></param> + /// <returns></returns> + [Tested] + public static bool operator ==(Rec<T1, T2, T3> record1, Rec<T1, T2, T3> record2) + { + return record1.Equals(record2); + } + /// <summary> + /// + /// </summary> + /// <param name="record1"></param> + /// <param name="record2"></param> + /// <returns></returns> + [Tested] + public static bool operator !=(Rec<T1, T2, T3> record1, Rec<T1, T2, T3> record2) + { + return !record1.Equals(record2); + } + /// <summary> + /// + /// </summary> + /// <returns></returns> + [Tested] + public override int GetHashCode() + { + //TODO: don't use 0 as hashcode for null, but something else! + int hashcode = X1 == null ? 0 : X1.GetHashCode(); + hashcode = hashcode * RecConst.HASHFACTOR + (X2 == null ? 0 : X2.GetHashCode()); + hashcode = hashcode * RecConst.HASHFACTOR + (X3 == null ? 0 : X3.GetHashCode()); + return hashcode; + } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public override string ToString() + { + return String.Format("({0}, {1}, {2})", X1, X2, X3); + } + #region IShowable Members + + /// <summary> + /// + /// </summary> + /// <param name="stringbuilder"></param> + /// <param name="rest"></param> + /// <param name="formatProvider"></param> + /// <returns></returns> + public bool Show(System.Text.StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider) + { + bool incomplete = true; + stringbuilder.Append("("); + rest -= 2; + try + { + if (incomplete = !Showing.Show(X1, stringbuilder, ref rest, formatProvider)) + return false; + stringbuilder.Append(", "); + rest -= 2; + if (incomplete = !Showing.Show(X2, stringbuilder, ref rest, formatProvider)) + return false; + stringbuilder.Append(", "); + rest -= 2; + if (incomplete = !Showing.Show(X3, stringbuilder, ref rest, formatProvider)) + return false; + } + finally + { + if (incomplete) + { + stringbuilder.Append("..."); + rest -= 3; + } + stringbuilder.Append(")"); + } + return true; + } + #endregion + + #region IFormattable Members + + /// <summary> + /// + /// </summary> + /// <param name="format"></param> + /// <param name="formatProvider"></param> + /// <returns></returns> + public string ToString(string format, IFormatProvider formatProvider) + { + return Showing.ShowString(this, format, formatProvider); + } + + #endregion + } + + /// <summary> + /// + /// </summary> + /// <typeparam name="T1"></typeparam> + /// <typeparam name="T2"></typeparam> + /// <typeparam name="T3"></typeparam> + /// <typeparam name="T4"></typeparam> + public struct Rec<T1, T2, T3, T4> : IEquatable<Rec<T1, T2, T3, T4>>, IShowable + { + /// <summary> + /// + /// </summary> + public readonly T1 X1; + /// <summary> + /// + /// </summary> + public readonly T2 X2; + /// <summary> + /// + /// </summary> + public readonly T3 X3; + /// <summary> + /// + /// </summary> + public readonly T4 X4; + /// <summary> + /// + /// </summary> + /// <param name="x1"></param> + /// <param name="x2"></param> + /// <param name="x3"></param> + /// <param name="x4"></param> + [Tested] + public Rec(T1 x1, T2 x2, T3 x3, T4 x4) + { + this.X1 = x1; this.X2 = x2; this.X3 = x3; this.X4 = x4; + } + /// <summary> + /// + /// </summary> + /// <param name="other"></param> + /// <returns></returns> + [Tested] + public bool Equals(Rec<T1, T2, T3, T4> other) + { + return + (X1 == null ? other.X1 == null : X1.Equals(other.X1)) && + (X2 == null ? other.X2 == null : X2.Equals(other.X2)) && + (X3 == null ? other.X3 == null : X3.Equals(other.X3)) && + (X4 == null ? other.X4 == null : X4.Equals(other.X4)) + ; + } + /// <summary> + /// + /// </summary> + /// <param name="obj"></param> + /// <returns></returns> + [Tested] + public override bool Equals(object obj) + { + return obj is Rec<T1, T2, T3, T4> ? Equals((Rec<T1, T2, T3, T4>)obj) : false; + } + + /// <summary> + /// + /// </summary> + /// <param name="record1"></param> + /// <param name="record2"></param> + /// <returns></returns> + [Tested] + public static bool operator ==(Rec<T1, T2, T3, T4> record1, Rec<T1, T2, T3, T4> record2) + { + return record1.Equals(record2); + } + /// <summary> + /// + /// </summary> + /// <param name="record1"></param> + /// <param name="record2"></param> + /// <returns></returns> + [Tested] + public static bool operator !=(Rec<T1, T2, T3, T4> record1, Rec<T1, T2, T3, T4> record2) + { + return !record1.Equals(record2); + } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + [Tested] + public override int GetHashCode() + { + //TODO: don't use 0 as hashcode for null, but something else! + int hashcode = X1 == null ? 0 : X1.GetHashCode(); + hashcode = hashcode * RecConst.HASHFACTOR + (X2 == null ? 0 : X2.GetHashCode()); + hashcode = hashcode * RecConst.HASHFACTOR + (X3 == null ? 0 : X3.GetHashCode()); + hashcode = hashcode * RecConst.HASHFACTOR + (X4 == null ? 0 : X4.GetHashCode()); + return hashcode; + } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public override string ToString() + { + return String.Format("({0}, {1}, {2}, {3})", X1, X2, X3, X4); + } + #region IShowable Members + + /// <summary> + /// + /// </summary> + /// <param name="stringbuilder"></param> + /// <param name="rest"></param> + /// <param name="formatProvider"></param> + /// <returns></returns> + public bool Show(System.Text.StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider) + { + bool incomplete = true; + stringbuilder.Append("("); + rest -= 2; + try + { + if (incomplete = !Showing.Show(X1, stringbuilder, ref rest, formatProvider)) + return false; + stringbuilder.Append(", "); + rest -= 2; + if (incomplete = !Showing.Show(X2, stringbuilder, ref rest, formatProvider)) + return false; + stringbuilder.Append(", "); + rest -= 2; + if (incomplete = !Showing.Show(X3, stringbuilder, ref rest, formatProvider)) + return false; + stringbuilder.Append(", "); + rest -= 2; + if (incomplete = !Showing.Show(X4, stringbuilder, ref rest, formatProvider)) + return false; + } + finally + { + if (incomplete) + { + stringbuilder.Append("..."); + rest -= 3; + } + stringbuilder.Append(")"); + } + return true; + } + #endregion + + #region IFormattable Members + + /// <summary> + /// + /// </summary> + /// <param name="format"></param> + /// <param name="formatProvider"></param> + /// <returns></returns> + public string ToString(string format, IFormatProvider formatProvider) + { + return Showing.ShowString(this, format, formatProvider); + } + + #endregion + } +} diff --git a/mcs/class/Mono.C5/C5/Sorting.cs b/mcs/class/Mono.C5/C5/Sorting.cs index d40003dd232..ed2ac484133 100644 --- a/mcs/class/Mono.C5/C5/Sorting.cs +++ b/mcs/class/Mono.C5/C5/Sorting.cs @@ -1,239 +1,239 @@ -/*
- Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-using System;
-using System.Diagnostics;
-using SCG = System.Collections.Generic;
-namespace C5
-{
- /// <summary>
- /// A utility class with functions for sorting arrays with respect to an IComparer<T>
- /// </summary>
- public class Sorting
- {
- Sorting() { }
-
- /// <summary>
- /// Sort part of array in place using IntroSort
- /// </summary>
- /// <exception cref="ArgumentOutOfRangeException">If the <code>start</code>
- /// and <code>count</code> arguments does not describe a valid range.</exception>
- /// <param name="array">Array to sort</param>
- /// <param name="start">Index of first position to sort</param>
- /// <param name="count">Number of elements to sort</param>
- /// <param name="comparer">IComparer<T> to sort by</param>
- [Tested]
- public static void IntroSort<T>(T[] array, int start, int count, SCG.IComparer<T> comparer)
- {
- if (start < 0 || count < 0 || start + count > array.Length)
- throw new ArgumentOutOfRangeException();
- new Sorter<T>(array, comparer).IntroSort(start, start + count);
- }
-
- /// <summary>
- /// Sort an array in place using IntroSort and default comparer
- /// </summary>
- /// <exception cref="NotComparableException">If T is not comparable</exception>
- /// <param name="array">Array to sort</param>
- [Tested]
- public static void IntroSort<T>(T[] array)
- {
- new Sorter<T>(array, Comparer<T>.Default).IntroSort(0, array.Length);
- }
-
-
- /// <summary>
- /// Sort part of array in place using Insertion Sort
- /// </summary>
- /// <exception cref="ArgumentOutOfRangeException">If the <code>start</code>
- /// and <code>count</code> arguments does not describe a valid range.</exception>
- /// <param name="array">Array to sort</param>
- /// <param name="start">Index of first position to sort</param>
- /// <param name="count">Number of elements to sort</param>
- /// <param name="comparer">IComparer<T> to sort by</param>
- [Tested]
- public static void InsertionSort<T>(T[] array, int start, int count, SCG.IComparer<T> comparer)
- {
- if (start < 0 || count < 0 || start + count > array.Length)
- throw new ArgumentOutOfRangeException();
- new Sorter<T>(array, comparer).InsertionSort(start, start + count);
- }
-
-
- /// <summary>
- /// Sort part of array in place using Heap Sort
- /// </summary>
- /// <exception cref="ArgumentOutOfRangeException">If the <code>start</code>
- /// and <code>count</code> arguments does not describe a valid range.</exception>
- /// <param name="array">Array to sort</param>
- /// <param name="start">Index of first position to sort</param>
- /// <param name="count">Number of elements to sort</param>
- /// <param name="comparer">IComparer<T> to sort by</param>
- [Tested]
- public static void HeapSort<T>(T[] array, int start, int count, SCG.IComparer<T> comparer)
- {
- if (start < 0 || count < 0 || start + count > array.Length)
- throw new ArgumentOutOfRangeException();
- new Sorter<T>(array, comparer).HeapSort(start, start + count);
- }
-
-
- class Sorter<T>
- {
- T[] a;
-
- SCG.IComparer<T> c;
-
-
- internal Sorter(T[] a, SCG.IComparer<T> c) { this.a = a; this.c = c; }
-
-
- internal void IntroSort(int f, int b)
- {
- if (b - f > 31)
- {
- int depth_limit = (int)Math.Floor(2.5 * Math.Log(b - f, 2));
-
- introSort(f, b, depth_limit);
- }
- else
- InsertionSort(f, b);
- }
-
-
- private void introSort(int f, int b, int depth_limit)
- {
- const int size_threshold = 14;//24;
-
- if (depth_limit-- == 0)
- HeapSort(f, b);
- else if (b - f <= size_threshold)
- InsertionSort(f, b);
- else
- {
- int p = partition(f, b);
-
- introSort(f, p, depth_limit);
- introSort(p, b, depth_limit);
- }
- }
-
-
- private int compare(T i1, T i2) { return c.Compare(i1, i2); }
-
-
- private int partition(int f, int b)
- {
- int bot = f, mid = (b + f) / 2, top = b - 1;
- T abot = a[bot], amid = a[mid], atop = a[top];
-
- if (compare(abot, amid) < 0)
- {
- if (compare(atop, abot) < 0)//atop<abot<amid
- { a[top] = amid; amid = a[mid] = abot; a[bot] = atop; }
- else if (compare(atop, amid) < 0) //abot<=atop<amid
- { a[top] = amid; amid = a[mid] = atop; }
- //else abot<amid<=atop
- }
- else
- {
- if (compare(amid, atop) > 0) //atop<amid<=abot
- { a[bot] = atop; a[top] = abot; }
- else if (compare(abot, atop) > 0) //amid<=atop<abot
- { a[bot] = amid; amid = a[mid] = atop; a[top] = abot; }
- else //amid<=abot<=atop
- { a[bot] = amid; amid = a[mid] = abot; }
- }
-
- int i = bot, j = top;
-
- while (true)
- {
- while (compare(a[++i], amid) < 0) ;
-
- while (compare(amid, a[--j]) < 0) ;
-
- if (i < j)
- {
- T tmp = a[i]; a[i] = a[j]; a[j] = tmp;
- }
- else
- return i;
- }
- }
-
-
- internal void InsertionSort(int f, int b)
- {
- for (int j = f + 1; j < b; j++)
- {
- T key = a[j], other;
- int i = j - 1;
-
- if (c.Compare(other = a[i], key) > 0)
- {
- a[j] = other;
- while (i > f && c.Compare(other = a[i - 1], key) > 0) { a[i--] = other; }
-
- a[i] = key;
- }
- }
- }
-
-
- internal void HeapSort(int f, int b)
- {
- for (int i = (b + f) / 2; i >= f; i--) heapify(f, b, i);
-
- for (int i = b - 1; i > f; i--)
- {
- T tmp = a[f]; a[f] = a[i]; a[i] = tmp;
- heapify(f, i, f);
- }
- }
-
-
- private void heapify(int f, int b, int i)
- {
- T pv = a[i], lv, rv, max = pv;
- int j = i, maxpt = j;
-
- while (true)
- {
- int l = 2 * j - f + 1, r = l + 1;
-
- if (l < b && compare(lv = a[l], max) > 0) { maxpt = l; max = lv; }
-
- if (r < b && compare(rv = a[r], max) > 0) { maxpt = r; max = rv; }
-
- if (maxpt == j)
- break;
-
- a[j] = max;
- max = pv;
- j = maxpt;
- }
-
- if (j > i)
- a[j] = pv;
- }
- }
- }
+/* + Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +using System; +using System.Diagnostics; +using SCG = System.Collections.Generic; +namespace C5 +{ + /// <summary> + /// A utility class with functions for sorting arrays with respect to an IComparer<T> + /// </summary> + public class Sorting + { + Sorting() { } + + /// <summary> + /// Sort part of array in place using IntroSort + /// </summary> + /// <exception cref="ArgumentOutOfRangeException">If the <code>start</code> + /// and <code>count</code> arguments does not describe a valid range.</exception> + /// <param name="array">Array to sort</param> + /// <param name="start">Index of first position to sort</param> + /// <param name="count">Number of elements to sort</param> + /// <param name="comparer">IComparer<T> to sort by</param> + [Tested] + public static void IntroSort<T>(T[] array, int start, int count, SCG.IComparer<T> comparer) + { + if (start < 0 || count < 0 || start + count > array.Length) + throw new ArgumentOutOfRangeException(); + new Sorter<T>(array, comparer).IntroSort(start, start + count); + } + + /// <summary> + /// Sort an array in place using IntroSort and default comparer + /// </summary> + /// <exception cref="NotComparableException">If T is not comparable</exception> + /// <param name="array">Array to sort</param> + [Tested] + public static void IntroSort<T>(T[] array) + { + new Sorter<T>(array, Comparer<T>.Default).IntroSort(0, array.Length); + } + + + /// <summary> + /// Sort part of array in place using Insertion Sort + /// </summary> + /// <exception cref="ArgumentOutOfRangeException">If the <code>start</code> + /// and <code>count</code> arguments does not describe a valid range.</exception> + /// <param name="array">Array to sort</param> + /// <param name="start">Index of first position to sort</param> + /// <param name="count">Number of elements to sort</param> + /// <param name="comparer">IComparer<T> to sort by</param> + [Tested] + public static void InsertionSort<T>(T[] array, int start, int count, SCG.IComparer<T> comparer) + { + if (start < 0 || count < 0 || start + count > array.Length) + throw new ArgumentOutOfRangeException(); + new Sorter<T>(array, comparer).InsertionSort(start, start + count); + } + + + /// <summary> + /// Sort part of array in place using Heap Sort + /// </summary> + /// <exception cref="ArgumentOutOfRangeException">If the <code>start</code> + /// and <code>count</code> arguments does not describe a valid range.</exception> + /// <param name="array">Array to sort</param> + /// <param name="start">Index of first position to sort</param> + /// <param name="count">Number of elements to sort</param> + /// <param name="comparer">IComparer<T> to sort by</param> + [Tested] + public static void HeapSort<T>(T[] array, int start, int count, SCG.IComparer<T> comparer) + { + if (start < 0 || count < 0 || start + count > array.Length) + throw new ArgumentOutOfRangeException(); + new Sorter<T>(array, comparer).HeapSort(start, start + count); + } + + + class Sorter<T> + { + T[] a; + + SCG.IComparer<T> c; + + + internal Sorter(T[] a, SCG.IComparer<T> c) { this.a = a; this.c = c; } + + + internal void IntroSort(int f, int b) + { + if (b - f > 31) + { + int depth_limit = (int)Math.Floor(2.5 * Math.Log(b - f, 2)); + + introSort(f, b, depth_limit); + } + else + InsertionSort(f, b); + } + + + private void introSort(int f, int b, int depth_limit) + { + const int size_threshold = 14;//24; + + if (depth_limit-- == 0) + HeapSort(f, b); + else if (b - f <= size_threshold) + InsertionSort(f, b); + else + { + int p = partition(f, b); + + introSort(f, p, depth_limit); + introSort(p, b, depth_limit); + } + } + + + private int compare(T i1, T i2) { return c.Compare(i1, i2); } + + + private int partition(int f, int b) + { + int bot = f, mid = (b + f) / 2, top = b - 1; + T abot = a[bot], amid = a[mid], atop = a[top]; + + if (compare(abot, amid) < 0) + { + if (compare(atop, abot) < 0)//atop<abot<amid + { a[top] = amid; amid = a[mid] = abot; a[bot] = atop; } + else if (compare(atop, amid) < 0) //abot<=atop<amid + { a[top] = amid; amid = a[mid] = atop; } + //else abot<amid<=atop + } + else + { + if (compare(amid, atop) > 0) //atop<amid<=abot + { a[bot] = atop; a[top] = abot; } + else if (compare(abot, atop) > 0) //amid<=atop<abot + { a[bot] = amid; amid = a[mid] = atop; a[top] = abot; } + else //amid<=abot<=atop + { a[bot] = amid; amid = a[mid] = abot; } + } + + int i = bot, j = top; + + while (true) + { + while (compare(a[++i], amid) < 0) ; + + while (compare(amid, a[--j]) < 0) ; + + if (i < j) + { + T tmp = a[i]; a[i] = a[j]; a[j] = tmp; + } + else + return i; + } + } + + + internal void InsertionSort(int f, int b) + { + for (int j = f + 1; j < b; j++) + { + T key = a[j], other; + int i = j - 1; + + if (c.Compare(other = a[i], key) > 0) + { + a[j] = other; + while (i > f && c.Compare(other = a[i - 1], key) > 0) { a[i--] = other; } + + a[i] = key; + } + } + } + + + internal void HeapSort(int f, int b) + { + for (int i = (b + f) / 2; i >= f; i--) heapify(f, b, i); + + for (int i = b - 1; i > f; i--) + { + T tmp = a[f]; a[f] = a[i]; a[i] = tmp; + heapify(f, i, f); + } + } + + + private void heapify(int f, int b, int i) + { + T pv = a[i], lv, rv, max = pv; + int j = i, maxpt = j; + + while (true) + { + int l = 2 * j - f + 1, r = l + 1; + + if (l < b && compare(lv = a[l], max) > 0) { maxpt = l; max = lv; } + + if (r < b && compare(rv = a[r], max) > 0) { maxpt = r; max = rv; } + + if (maxpt == j) + break; + + a[j] = max; + max = pv; + j = maxpt; + } + + if (j > i) + a[j] = pv; + } + } + } }
\ No newline at end of file diff --git a/mcs/class/Mono.C5/C5/ViewSupport.cs b/mcs/class/Mono.C5/C5/ViewSupport.cs index ca2a6d72d30..0e0cb233358 100644 --- a/mcs/class/Mono.C5/C5/ViewSupport.cs +++ b/mcs/class/Mono.C5/C5/ViewSupport.cs @@ -1,101 +1,101 @@ -/*
- Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-
-using System;
-using System.Diagnostics;
-using SCG = System.Collections.Generic;
-
-namespace C5
-{
- /// <summary>
- /// Characterize the mutual position of some view B (other) relative to view A (this)
- /// </summary>
- enum MutualViewPosition
- {
- /// <summary>
- /// B contains A(this)
- /// </summary>
- Contains,
- /// <summary>
- /// B is containd in A(this), but not vice versa
- /// </summary>
- ContainedIn,
- /// <summary>
- /// A and B does not overlap
- /// </summary>
- NonOverlapping,
- /// <summary>
- /// A and B overlap, but neither is contained in the other
- /// </summary>
- Overlapping
- }
-
- #region View List Nested class
- /// <summary>
- /// This class is shared between the linked list and array list implementations.
- /// </summary>
- /// <typeparam name="V"></typeparam>
- [Serializable]
- class WeakViewList<V> where V : class
- {
- Node start;
- [Serializable]
- internal class Node
- {
- internal WeakReference weakview; internal Node prev, next;
- internal Node(V view) { weakview = new WeakReference(view); }
- }
- internal Node Add(V view)
- {
- Node newNode = new Node(view);
- if (start != null) { start.prev = newNode; newNode.next = start; }
- start = newNode;
- return newNode;
- }
- internal void Remove(Node n)
- {
- if (n == start) { start = start.next; if (start != null) start.prev = null; }
- else { n.prev.next = n.next; if (n.next != null) n.next.prev = n.prev; }
- }
- /// <summary>
- /// Note that it is safe to call views.Remove(view.myWeakReference) if view
- /// is the currently yielded object
- /// </summary>
- /// <returns></returns>
- public SCG.IEnumerator<V> GetEnumerator()
- {
- Node n = start;
- while (n != null)
- {
- //V view = n.weakview.Target as V; //This provokes a bug in the beta1 verifyer
- object o = n.weakview.Target;
- V view = o is V ? (V)o : null;
- if (view == null)
- Remove(n);
- else
- yield return view;
- n = n.next;
- }
- }
- }
-
- #endregion
+/* + Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +using System; +using System.Diagnostics; +using SCG = System.Collections.Generic; + +namespace C5 +{ + /// <summary> + /// Characterize the mutual position of some view B (other) relative to view A (this) + /// </summary> + enum MutualViewPosition + { + /// <summary> + /// B contains A(this) + /// </summary> + Contains, + /// <summary> + /// B is containd in A(this), but not vice versa + /// </summary> + ContainedIn, + /// <summary> + /// A and B does not overlap + /// </summary> + NonOverlapping, + /// <summary> + /// A and B overlap, but neither is contained in the other + /// </summary> + Overlapping + } + + #region View List Nested class + /// <summary> + /// This class is shared between the linked list and array list implementations. + /// </summary> + /// <typeparam name="V"></typeparam> + [Serializable] + class WeakViewList<V> where V : class + { + Node start; + [Serializable] + internal class Node + { + internal WeakReference weakview; internal Node prev, next; + internal Node(V view) { weakview = new WeakReference(view); } + } + internal Node Add(V view) + { + Node newNode = new Node(view); + if (start != null) { start.prev = newNode; newNode.next = start; } + start = newNode; + return newNode; + } + internal void Remove(Node n) + { + if (n == start) { start = start.next; if (start != null) start.prev = null; } + else { n.prev.next = n.next; if (n.next != null) n.next.prev = n.prev; } + } + /// <summary> + /// Note that it is safe to call views.Remove(view.myWeakReference) if view + /// is the currently yielded object + /// </summary> + /// <returns></returns> + public SCG.IEnumerator<V> GetEnumerator() + { + Node n = start; + while (n != null) + { + //V view = n.weakview.Target as V; //This provokes a bug in the beta1 verifyer + object o = n.weakview.Target; + V view = o is V ? (V)o : null; + if (view == null) + Remove(n); + else + yield return view; + n = n.next; + } + } + } + + #endregion }
\ No newline at end of file diff --git a/mcs/class/Mono.C5/C5/WrappedArray.cs b/mcs/class/Mono.C5/C5/WrappedArray.cs index 1eb61beaef1..d275625a392 100644 --- a/mcs/class/Mono.C5/C5/WrappedArray.cs +++ b/mcs/class/Mono.C5/C5/WrappedArray.cs @@ -1,944 +1,944 @@ -/*
- Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-
-using System;
-using System.Text;
-using System.Diagnostics;
-using SCG = System.Collections.Generic;
-namespace C5
-{
- /// <summary>
- /// An advanced interface to operations on an array. The array is viewed as an
- /// <see cref="T:C5.IList`1"/> of fixed size, and so all operations that would change the
- /// size of the array will be invalid (and throw <see cref="T:C5.FixedSizeCollectionException"/>
- /// </summary>
- /// <typeparam name="T"></typeparam>
- public class WrappedArray<T> : IList<T>, SCG.IList<T>
- {
- class InnerList : ArrayList<T>
- {
- internal InnerList(T[] array) { this.array = array; size = array.Length; }
- }
- ArrayList<T> innerlist;
- //TODO: remember a ref to the wrapped array in WrappedArray to save a little on indexing?
- WrappedArray<T> underlying;
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="wrappedarray"></param>
- public WrappedArray(T[] wrappedarray) { innerlist = new InnerList(wrappedarray); }
-
- //for views
- WrappedArray(ArrayList<T> arraylist, WrappedArray<T> underlying) { innerlist = arraylist; this.underlying = underlying; }
-
- #region IList<T> Members
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public T First { get { return innerlist.First; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public T Last { get { return innerlist.Last; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="index"></param>
- /// <returns></returns>
- public T this[int index]
- {
- get { return innerlist[index]; }
- set { innerlist[index] = value; }
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="filter"></param>
- /// <returns></returns>
- public IList<T> FindAll(Fun<T, bool> filter) { return innerlist.FindAll(filter); }
-
- /// <summary>
- ///
- /// </summary>
- /// <typeparam name="V"></typeparam>
- /// <param name="mapper"></param>
- /// <returns></returns>
- public IList<V> Map<V>(Fun<T, V> mapper) { return innerlist.Map<V>(mapper); }
-
- /// <summary>
- ///
- /// </summary>
- /// <typeparam name="V"></typeparam>
- /// <param name="mapper"></param>
- /// <param name="equalityComparer"></param>
- /// <returns></returns>
- public IList<V> Map<V>(Fun<T, V> mapper, SCG.IEqualityComparer<V> equalityComparer) { return innerlist.Map<V>(mapper, equalityComparer); }
-
- /// <summary>
- /// ???? should we throw NotRelevantException
- /// </summary>
- /// <value></value>
- public bool FIFO
- {
- get { throw new FixedSizeCollectionException(); }
- set { throw new FixedSizeCollectionException(); }
- }
-
- /// <summary>
- ///
- /// </summary>
- public virtual bool IsFixedSize
- {
- get { return true; }
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="index"></param>
- /// <param name="item"></param>
- public void Insert(int index, T item)
- {
- throw new FixedSizeCollectionException();
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="pointer"></param>
- /// <param name="item"></param>
- public void Insert(IList<T> pointer, T item)
- {
- throw new FixedSizeCollectionException();
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- public void InsertFirst(T item)
- {
- throw new FixedSizeCollectionException();
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- public void InsertLast(T item)
- {
- throw new FixedSizeCollectionException();
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <typeparam name="U"></typeparam>
- /// <param name="i"></param>
- /// <param name="items"></param>
- public void InsertAll<U>(int i, System.Collections.Generic.IEnumerable<U> items) where U : T
- {
- throw new FixedSizeCollectionException();
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public T Remove()
- {
- throw new FixedSizeCollectionException();
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public T RemoveFirst()
- {
- throw new FixedSizeCollectionException();
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public T RemoveLast()
- {
- throw new FixedSizeCollectionException();
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="start"></param>
- /// <param name="count"></param>
- /// <returns></returns>
- public IList<T> View(int start, int count)
- {
- return new WrappedArray<T>((ArrayList<T>)innerlist.View(start, count), underlying ?? this);
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <returns></returns>
- public IList<T> ViewOf(T item)
- {
- return new WrappedArray<T>((ArrayList<T>)innerlist.ViewOf(item), underlying ?? this);
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <returns></returns>
- public IList<T> LastViewOf(T item)
- {
- return new WrappedArray<T>((ArrayList<T>)innerlist.LastViewOf(item), underlying ?? this);
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public IList<T> Underlying { get { return underlying; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public int Offset { get { return innerlist.Offset; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public bool IsValid { get { return innerlist.IsValid; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="offset"></param>
- /// <returns></returns>
- public IList<T> Slide(int offset) { return innerlist.Slide(offset); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="offset"></param>
- /// <param name="size"></param>
- /// <returns></returns>
- public IList<T> Slide(int offset, int size) { return innerlist.Slide(offset, size); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="offset"></param>
- /// <returns></returns>
- public bool TrySlide(int offset) { return innerlist.TrySlide(offset); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="offset"></param>
- /// <param name="size"></param>
- /// <returns></returns>
- public bool TrySlide(int offset, int size) { return innerlist.TrySlide(offset, size); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="otherView"></param>
- /// <returns></returns>
- public IList<T> Span(IList<T> otherView) { return innerlist.Span(((WrappedArray<T>)otherView).innerlist); }
-
- /// <summary>
- ///
- /// </summary>
- public void Reverse() { innerlist.Reverse(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public bool IsSorted() { return innerlist.IsSorted(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="comparer"></param>
- /// <returns></returns>
- public bool IsSorted(SCG.IComparer<T> comparer) { return innerlist.IsSorted(comparer); }
-
- /// <summary>
- ///
- /// </summary>
- public void Sort() { innerlist.Sort(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="comparer"></param>
- public void Sort(SCG.IComparer<T> comparer) { innerlist.Sort(comparer); }
-
- /// <summary>
- ///
- /// </summary>
- public void Shuffle() { innerlist.Shuffle(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="rnd"></param>
- public void Shuffle(Random rnd) { innerlist.Shuffle(rnd); }
-
- #endregion
-
- #region IIndexed<T> Members
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public Speed IndexingSpeed { get { return Speed.Constant; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="start"></param>
- /// <param name="count"></param>
- /// <returns></returns>
- public IDirectedCollectionValue<T> this[int start, int count] { get { return innerlist[start, count]; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <returns></returns>
- public int IndexOf(T item) { return innerlist.IndexOf(item); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <returns></returns>
- public int LastIndexOf(T item) { return innerlist.LastIndexOf(item); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="predicate"></param>
- /// <returns></returns>
- public int FindIndex(Fun<T, bool> predicate) { return innerlist.FindIndex(predicate); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="predicate"></param>
- /// <returns></returns>
- public int FindLastIndex(Fun<T, bool> predicate) { return innerlist.FindLastIndex(predicate); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="i"></param>
- /// <returns></returns>
- public T RemoveAt(int i) { throw new FixedSizeCollectionException(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="start"></param>
- /// <param name="count"></param>
- public void RemoveInterval(int start, int count) { throw new FixedSizeCollectionException(); }
-
- #endregion
-
- #region ISequenced<T> Members
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public int GetSequencedHashCode() { return innerlist.GetSequencedHashCode(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="that"></param>
- /// <returns></returns>
- public bool SequencedEquals(ISequenced<T> that) { return innerlist.SequencedEquals(that); }
-
- #endregion
-
- #region ICollection<T> Members
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public Speed ContainsSpeed { get { return Speed.Linear; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public int GetUnsequencedHashCode() { return innerlist.GetUnsequencedHashCode(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="that"></param>
- /// <returns></returns>
- public bool UnsequencedEquals(ICollection<T> that) { return innerlist.UnsequencedEquals(that); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <returns></returns>
- public bool Contains(T item) { return innerlist.Contains(item); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <returns></returns>
- public int ContainsCount(T item) { return innerlist.ContainsCount(item); }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public ICollectionValue<T> UniqueItems() { return innerlist.UniqueItems(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public ICollectionValue<KeyValuePair<T, int>> ItemMultiplicities() { return innerlist.ItemMultiplicities(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <typeparam name="U"></typeparam>
- /// <param name="items"></param>
- /// <returns></returns>
- public bool ContainsAll<U>(System.Collections.Generic.IEnumerable<U> items) where U : T
- { return innerlist.ContainsAll(items); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <returns></returns>
- public bool Find(ref T item) { return innerlist.Find(ref item); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <returns></returns>
- public bool FindOrAdd(ref T item) { throw new FixedSizeCollectionException(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <returns></returns>
- public bool Update(T item) { throw new FixedSizeCollectionException(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <param name="olditem"></param>
- /// <returns></returns>
- public bool Update(T item, out T olditem) { throw new FixedSizeCollectionException(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <returns></returns>
- public bool UpdateOrAdd(T item) { throw new FixedSizeCollectionException(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <param name="olditem"></param>
- /// <returns></returns>
- public bool UpdateOrAdd(T item, out T olditem) { throw new FixedSizeCollectionException(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <returns></returns>
- public bool Remove(T item) { throw new FixedSizeCollectionException(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <param name="removeditem"></param>
- /// <returns></returns>
- public bool Remove(T item, out T removeditem) { throw new FixedSizeCollectionException(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- public void RemoveAllCopies(T item) { throw new FixedSizeCollectionException(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <typeparam name="U"></typeparam>
- /// <param name="items"></param>
- public void RemoveAll<U>(System.Collections.Generic.IEnumerable<U> items) where U : T { throw new FixedSizeCollectionException(); }
-
- /// <summary>
- ///
- /// </summary>
- public void Clear() { throw new FixedSizeCollectionException(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <typeparam name="U"></typeparam>
- /// <param name="items"></param>
- public void RetainAll<U>(System.Collections.Generic.IEnumerable<U> items) where U : T { throw new FixedSizeCollectionException(); }
-
- #endregion
-
- #region IExtensible<T> Members
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public bool IsReadOnly { get { return true; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public bool AllowsDuplicates
- {
- get { return true; }
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public SCG.IEqualityComparer<T> EqualityComparer { get { return innerlist.EqualityComparer; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public bool DuplicatesByCounting
- {
- get { return false; }
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <returns></returns>
- public bool Add(T item)
- {
- throw new FixedSizeCollectionException();
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <typeparam name="U"></typeparam>
- /// <param name="items"></param>
- public void AddAll<U>(System.Collections.Generic.IEnumerable<U> items) where U : T
- {
- throw new FixedSizeCollectionException();
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public bool Check()
- {
- return innerlist.Check() && (underlying == null || underlying.innerlist == innerlist.Underlying);
- }
-
- #endregion
-
- #region ICollectionValue<T> Members
- /// <summary>
- /// No listeners may be installed
- /// </summary>
- /// <value>0</value>
- public virtual EventTypeEnum ListenableEvents { get { return 0; } }
-
- /// <summary>
- /// No listeners ever installed
- /// </summary>
- /// <value>0</value>
- public virtual EventTypeEnum ActiveEvents { get { return 0; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public event CollectionChangedHandler<T> CollectionChanged
- {
- add { throw new UnlistenableEventException(); }
- remove { throw new UnlistenableEventException(); }
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public event CollectionClearedHandler<T> CollectionCleared
- {
- add { throw new UnlistenableEventException(); }
- remove { throw new UnlistenableEventException(); }
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public event ItemsAddedHandler<T> ItemsAdded
- {
- add { throw new UnlistenableEventException(); }
- remove { throw new UnlistenableEventException(); }
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public event ItemInsertedHandler<T> ItemInserted
- {
- add { throw new UnlistenableEventException(); }
- remove { throw new UnlistenableEventException(); }
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public event ItemsRemovedHandler<T> ItemsRemoved
- {
- add { throw new UnlistenableEventException(); }
- remove { throw new UnlistenableEventException(); }
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public event ItemRemovedAtHandler<T> ItemRemovedAt
- {
- add { throw new UnlistenableEventException(); }
- remove { throw new UnlistenableEventException(); }
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public bool IsEmpty { get { return innerlist.IsEmpty; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public int Count { get { return innerlist.Count; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public Speed CountSpeed { get { return innerlist.CountSpeed; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="array"></param>
- /// <param name="index"></param>
- public void CopyTo(T[] array, int index) { innerlist.CopyTo(array, index); }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public T[] ToArray() { return innerlist.ToArray(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="action"></param>
- public void Apply(Act<T> action) { innerlist.Apply(action); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="predicate"></param>
- /// <returns></returns>
- public bool Exists(Fun<T, bool> predicate) { return innerlist.Exists(predicate); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="predicate"></param>
- /// <param name="item"></param>
- /// <returns></returns>
- public bool Find(Fun<T, bool> predicate, out T item) { return innerlist.Find(predicate, out item); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="predicate"></param>
- /// <returns></returns>
- public bool All(Fun<T, bool> predicate) { return innerlist.All(predicate); }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public T Choose() { return innerlist.Choose(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="filter"></param>
- /// <returns></returns>
- public SCG.IEnumerable<T> Filter(Fun<T, bool> filter) { return innerlist.Filter(filter); }
-
- #endregion
-
- #region IEnumerable<T> Members
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public SCG.IEnumerator<T> GetEnumerator() { return innerlist.GetEnumerator(); }
- #endregion
-
- #region IShowable Members
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="stringbuilder"></param>
- /// <param name="rest"></param>
- /// <param name="formatProvider"></param>
- /// <returns></returns>
- public bool Show(StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider)
- { return innerlist.Show(stringbuilder, ref rest, formatProvider); }
-
- #endregion
-
- #region IFormattable Members
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public override string ToString() { return innerlist.ToString(); }
-
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="format"></param>
- /// <param name="formatProvider"></param>
- /// <returns></returns>
- public virtual string ToString(string format, IFormatProvider formatProvider) { return innerlist.ToString(format, formatProvider); }
-
- #endregion
-
- #region IDirectedCollectionValue<T> Members
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public IDirectedCollectionValue<T> Backwards() { return innerlist.Backwards(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="predicate"></param>
- /// <param name="item"></param>
- /// <returns></returns>
- public bool FindLast(Fun<T, bool> predicate, out T item) { return innerlist.FindLast(predicate, out item); }
-
- #endregion
-
- #region IDirectedEnumerable<T> Members
-
- IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return Backwards(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public EnumerationDirection Direction { get { return EnumerationDirection.Forwards; } }
-
- #endregion
-
- #region IDisposable Members
-
- /// <summary>
- /// Dispose this if a view else operation is illegal
- /// </summary>
- /// <exception cref="FixedSizeCollectionException">If not a view</exception>
- public void Dispose()
- {
- if (underlying == null)
- throw new FixedSizeCollectionException();
- else
- innerlist.Dispose();
- }
-
- #endregion
-
- #region ICloneable Members
-
- /// <summary>
- /// Make a shallow copy of this WrappedArray.
- ///
- ///
- /// </summary>
- /// <returns></returns>
- public virtual object Clone()
- {
- return new WrappedArray<T>(innerlist.ToArray());
- }
-
- #endregion
-
- #region System.Collections.Generic.IList<T> Members
-
- void System.Collections.Generic.IList<T>.RemoveAt(int index)
- {
- throw new FixedSizeCollectionException();
- }
-
- void System.Collections.Generic.ICollection<T>.Add(T item)
- {
- throw new FixedSizeCollectionException();
- }
-
- #endregion
-
- #region System.Collections.ICollection Members
-
- bool System.Collections.ICollection.IsSynchronized
- {
- get { return false; }
- }
-
- [Obsolete]
- Object System.Collections.ICollection.SyncRoot
- {
- get { return ((System.Collections.IList)innerlist).SyncRoot; }
- }
-
- void System.Collections.ICollection.CopyTo(Array arr, int index)
- {
- if (index < 0 || index + Count > arr.Length)
- throw new ArgumentOutOfRangeException();
-
- foreach (T item in this)
- arr.SetValue(item, index++);
- }
-
- #endregion
-
- #region System.Collections.IList Members
-
- Object System.Collections.IList.this[int index]
- {
- get { return this[index]; }
- set { this[index] = (T)value; }
- }
-
- int System.Collections.IList.Add(Object o)
- {
- bool added = Add((T)o);
- // What position to report if item not added? SC.IList.Add doesn't say
- return added ? Count - 1 : -1;
- }
-
- bool System.Collections.IList.Contains(Object o)
- {
- return Contains((T)o);
- }
-
- int System.Collections.IList.IndexOf(Object o)
- {
- return Math.Max(-1, IndexOf((T)o));
- }
-
- void System.Collections.IList.Insert(int index, Object o)
- {
- Insert(index, (T)o);
- }
-
- void System.Collections.IList.Remove(Object o)
- {
- Remove((T)o);
- }
-
- void System.Collections.IList.RemoveAt(int index)
- {
- RemoveAt(index);
- }
-
- #endregion
-
- #region IEnumerable Members
-
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
- {
- throw new Exception("The method or operation is not implemented.");
- }
-
- #endregion
- }
+/* + Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +using System; +using System.Text; +using System.Diagnostics; +using SCG = System.Collections.Generic; +namespace C5 +{ + /// <summary> + /// An advanced interface to operations on an array. The array is viewed as an + /// <see cref="T:C5.IList`1"/> of fixed size, and so all operations that would change the + /// size of the array will be invalid (and throw <see cref="T:C5.FixedSizeCollectionException"/> + /// </summary> + /// <typeparam name="T"></typeparam> + public class WrappedArray<T> : IList<T>, SCG.IList<T> + { + class InnerList : ArrayList<T> + { + internal InnerList(T[] array) { this.array = array; size = array.Length; } + } + ArrayList<T> innerlist; + //TODO: remember a ref to the wrapped array in WrappedArray to save a little on indexing? + WrappedArray<T> underlying; + + /// <summary> + /// + /// </summary> + /// <param name="wrappedarray"></param> + public WrappedArray(T[] wrappedarray) { innerlist = new InnerList(wrappedarray); } + + //for views + WrappedArray(ArrayList<T> arraylist, WrappedArray<T> underlying) { innerlist = arraylist; this.underlying = underlying; } + + #region IList<T> Members + + /// <summary> + /// + /// </summary> + /// <value></value> + public T First { get { return innerlist.First; } } + + /// <summary> + /// + /// </summary> + /// <value></value> + public T Last { get { return innerlist.Last; } } + + /// <summary> + /// + /// </summary> + /// <param name="index"></param> + /// <returns></returns> + public T this[int index] + { + get { return innerlist[index]; } + set { innerlist[index] = value; } + } + + /// <summary> + /// + /// </summary> + /// <param name="filter"></param> + /// <returns></returns> + public IList<T> FindAll(Fun<T, bool> filter) { return innerlist.FindAll(filter); } + + /// <summary> + /// + /// </summary> + /// <typeparam name="V"></typeparam> + /// <param name="mapper"></param> + /// <returns></returns> + public IList<V> Map<V>(Fun<T, V> mapper) { return innerlist.Map<V>(mapper); } + + /// <summary> + /// + /// </summary> + /// <typeparam name="V"></typeparam> + /// <param name="mapper"></param> + /// <param name="equalityComparer"></param> + /// <returns></returns> + public IList<V> Map<V>(Fun<T, V> mapper, SCG.IEqualityComparer<V> equalityComparer) { return innerlist.Map<V>(mapper, equalityComparer); } + + /// <summary> + /// ???? should we throw NotRelevantException + /// </summary> + /// <value></value> + public bool FIFO + { + get { throw new FixedSizeCollectionException(); } + set { throw new FixedSizeCollectionException(); } + } + + /// <summary> + /// + /// </summary> + public virtual bool IsFixedSize + { + get { return true; } + } + + /// <summary> + /// + /// </summary> + /// <param name="index"></param> + /// <param name="item"></param> + public void Insert(int index, T item) + { + throw new FixedSizeCollectionException(); + } + + /// <summary> + /// + /// </summary> + /// <param name="pointer"></param> + /// <param name="item"></param> + public void Insert(IList<T> pointer, T item) + { + throw new FixedSizeCollectionException(); + } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + public void InsertFirst(T item) + { + throw new FixedSizeCollectionException(); + } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + public void InsertLast(T item) + { + throw new FixedSizeCollectionException(); + } + + /// <summary> + /// + /// </summary> + /// <typeparam name="U"></typeparam> + /// <param name="i"></param> + /// <param name="items"></param> + public void InsertAll<U>(int i, System.Collections.Generic.IEnumerable<U> items) where U : T + { + throw new FixedSizeCollectionException(); + } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public T Remove() + { + throw new FixedSizeCollectionException(); + } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public T RemoveFirst() + { + throw new FixedSizeCollectionException(); + } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public T RemoveLast() + { + throw new FixedSizeCollectionException(); + } + + /// <summary> + /// + /// </summary> + /// <param name="start"></param> + /// <param name="count"></param> + /// <returns></returns> + public IList<T> View(int start, int count) + { + return new WrappedArray<T>((ArrayList<T>)innerlist.View(start, count), underlying ?? this); + } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <returns></returns> + public IList<T> ViewOf(T item) + { + return new WrappedArray<T>((ArrayList<T>)innerlist.ViewOf(item), underlying ?? this); + } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <returns></returns> + public IList<T> LastViewOf(T item) + { + return new WrappedArray<T>((ArrayList<T>)innerlist.LastViewOf(item), underlying ?? this); + } + + /// <summary> + /// + /// </summary> + /// <value></value> + public IList<T> Underlying { get { return underlying; } } + + /// <summary> + /// + /// </summary> + /// <value></value> + public int Offset { get { return innerlist.Offset; } } + + /// <summary> + /// + /// </summary> + /// <value></value> + public bool IsValid { get { return innerlist.IsValid; } } + + /// <summary> + /// + /// </summary> + /// <param name="offset"></param> + /// <returns></returns> + public IList<T> Slide(int offset) { return innerlist.Slide(offset); } + + /// <summary> + /// + /// </summary> + /// <param name="offset"></param> + /// <param name="size"></param> + /// <returns></returns> + public IList<T> Slide(int offset, int size) { return innerlist.Slide(offset, size); } + + /// <summary> + /// + /// </summary> + /// <param name="offset"></param> + /// <returns></returns> + public bool TrySlide(int offset) { return innerlist.TrySlide(offset); } + + /// <summary> + /// + /// </summary> + /// <param name="offset"></param> + /// <param name="size"></param> + /// <returns></returns> + public bool TrySlide(int offset, int size) { return innerlist.TrySlide(offset, size); } + + /// <summary> + /// + /// </summary> + /// <param name="otherView"></param> + /// <returns></returns> + public IList<T> Span(IList<T> otherView) { return innerlist.Span(((WrappedArray<T>)otherView).innerlist); } + + /// <summary> + /// + /// </summary> + public void Reverse() { innerlist.Reverse(); } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public bool IsSorted() { return innerlist.IsSorted(); } + + /// <summary> + /// + /// </summary> + /// <param name="comparer"></param> + /// <returns></returns> + public bool IsSorted(SCG.IComparer<T> comparer) { return innerlist.IsSorted(comparer); } + + /// <summary> + /// + /// </summary> + public void Sort() { innerlist.Sort(); } + + /// <summary> + /// + /// </summary> + /// <param name="comparer"></param> + public void Sort(SCG.IComparer<T> comparer) { innerlist.Sort(comparer); } + + /// <summary> + /// + /// </summary> + public void Shuffle() { innerlist.Shuffle(); } + + /// <summary> + /// + /// </summary> + /// <param name="rnd"></param> + public void Shuffle(Random rnd) { innerlist.Shuffle(rnd); } + + #endregion + + #region IIndexed<T> Members + + /// <summary> + /// + /// </summary> + /// <value></value> + public Speed IndexingSpeed { get { return Speed.Constant; } } + + /// <summary> + /// + /// </summary> + /// <param name="start"></param> + /// <param name="count"></param> + /// <returns></returns> + public IDirectedCollectionValue<T> this[int start, int count] { get { return innerlist[start, count]; } } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <returns></returns> + public int IndexOf(T item) { return innerlist.IndexOf(item); } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <returns></returns> + public int LastIndexOf(T item) { return innerlist.LastIndexOf(item); } + + /// <summary> + /// + /// </summary> + /// <param name="predicate"></param> + /// <returns></returns> + public int FindIndex(Fun<T, bool> predicate) { return innerlist.FindIndex(predicate); } + + /// <summary> + /// + /// </summary> + /// <param name="predicate"></param> + /// <returns></returns> + public int FindLastIndex(Fun<T, bool> predicate) { return innerlist.FindLastIndex(predicate); } + + /// <summary> + /// + /// </summary> + /// <param name="i"></param> + /// <returns></returns> + public T RemoveAt(int i) { throw new FixedSizeCollectionException(); } + + /// <summary> + /// + /// </summary> + /// <param name="start"></param> + /// <param name="count"></param> + public void RemoveInterval(int start, int count) { throw new FixedSizeCollectionException(); } + + #endregion + + #region ISequenced<T> Members + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public int GetSequencedHashCode() { return innerlist.GetSequencedHashCode(); } + + /// <summary> + /// + /// </summary> + /// <param name="that"></param> + /// <returns></returns> + public bool SequencedEquals(ISequenced<T> that) { return innerlist.SequencedEquals(that); } + + #endregion + + #region ICollection<T> Members + /// <summary> + /// + /// </summary> + /// <value></value> + public Speed ContainsSpeed { get { return Speed.Linear; } } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public int GetUnsequencedHashCode() { return innerlist.GetUnsequencedHashCode(); } + + /// <summary> + /// + /// </summary> + /// <param name="that"></param> + /// <returns></returns> + public bool UnsequencedEquals(ICollection<T> that) { return innerlist.UnsequencedEquals(that); } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <returns></returns> + public bool Contains(T item) { return innerlist.Contains(item); } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <returns></returns> + public int ContainsCount(T item) { return innerlist.ContainsCount(item); } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public ICollectionValue<T> UniqueItems() { return innerlist.UniqueItems(); } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public ICollectionValue<KeyValuePair<T, int>> ItemMultiplicities() { return innerlist.ItemMultiplicities(); } + + /// <summary> + /// + /// </summary> + /// <typeparam name="U"></typeparam> + /// <param name="items"></param> + /// <returns></returns> + public bool ContainsAll<U>(System.Collections.Generic.IEnumerable<U> items) where U : T + { return innerlist.ContainsAll(items); } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <returns></returns> + public bool Find(ref T item) { return innerlist.Find(ref item); } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <returns></returns> + public bool FindOrAdd(ref T item) { throw new FixedSizeCollectionException(); } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <returns></returns> + public bool Update(T item) { throw new FixedSizeCollectionException(); } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <param name="olditem"></param> + /// <returns></returns> + public bool Update(T item, out T olditem) { throw new FixedSizeCollectionException(); } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <returns></returns> + public bool UpdateOrAdd(T item) { throw new FixedSizeCollectionException(); } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <param name="olditem"></param> + /// <returns></returns> + public bool UpdateOrAdd(T item, out T olditem) { throw new FixedSizeCollectionException(); } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <returns></returns> + public bool Remove(T item) { throw new FixedSizeCollectionException(); } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <param name="removeditem"></param> + /// <returns></returns> + public bool Remove(T item, out T removeditem) { throw new FixedSizeCollectionException(); } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + public void RemoveAllCopies(T item) { throw new FixedSizeCollectionException(); } + + /// <summary> + /// + /// </summary> + /// <typeparam name="U"></typeparam> + /// <param name="items"></param> + public void RemoveAll<U>(System.Collections.Generic.IEnumerable<U> items) where U : T { throw new FixedSizeCollectionException(); } + + /// <summary> + /// + /// </summary> + public void Clear() { throw new FixedSizeCollectionException(); } + + /// <summary> + /// + /// </summary> + /// <typeparam name="U"></typeparam> + /// <param name="items"></param> + public void RetainAll<U>(System.Collections.Generic.IEnumerable<U> items) where U : T { throw new FixedSizeCollectionException(); } + + #endregion + + #region IExtensible<T> Members + + /// <summary> + /// + /// </summary> + /// <value></value> + public bool IsReadOnly { get { return true; } } + + /// <summary> + /// + /// </summary> + /// <value></value> + public bool AllowsDuplicates + { + get { return true; } + } + + /// <summary> + /// + /// </summary> + /// <value></value> + public SCG.IEqualityComparer<T> EqualityComparer { get { return innerlist.EqualityComparer; } } + + /// <summary> + /// + /// </summary> + /// <value></value> + public bool DuplicatesByCounting + { + get { return false; } + } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <returns></returns> + public bool Add(T item) + { + throw new FixedSizeCollectionException(); + } + + /// <summary> + /// + /// </summary> + /// <typeparam name="U"></typeparam> + /// <param name="items"></param> + public void AddAll<U>(System.Collections.Generic.IEnumerable<U> items) where U : T + { + throw new FixedSizeCollectionException(); + } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public bool Check() + { + return innerlist.Check() && (underlying == null || underlying.innerlist == innerlist.Underlying); + } + + #endregion + + #region ICollectionValue<T> Members + /// <summary> + /// No listeners may be installed + /// </summary> + /// <value>0</value> + public virtual EventTypeEnum ListenableEvents { get { return 0; } } + + /// <summary> + /// No listeners ever installed + /// </summary> + /// <value>0</value> + public virtual EventTypeEnum ActiveEvents { get { return 0; } } + + /// <summary> + /// + /// </summary> + /// <value></value> + public event CollectionChangedHandler<T> CollectionChanged + { + add { throw new UnlistenableEventException(); } + remove { throw new UnlistenableEventException(); } + } + + /// <summary> + /// + /// </summary> + /// <value></value> + public event CollectionClearedHandler<T> CollectionCleared + { + add { throw new UnlistenableEventException(); } + remove { throw new UnlistenableEventException(); } + } + + /// <summary> + /// + /// </summary> + /// <value></value> + public event ItemsAddedHandler<T> ItemsAdded + { + add { throw new UnlistenableEventException(); } + remove { throw new UnlistenableEventException(); } + } + + /// <summary> + /// + /// </summary> + /// <value></value> + public event ItemInsertedHandler<T> ItemInserted + { + add { throw new UnlistenableEventException(); } + remove { throw new UnlistenableEventException(); } + } + + /// <summary> + /// + /// </summary> + /// <value></value> + public event ItemsRemovedHandler<T> ItemsRemoved + { + add { throw new UnlistenableEventException(); } + remove { throw new UnlistenableEventException(); } + } + + /// <summary> + /// + /// </summary> + /// <value></value> + public event ItemRemovedAtHandler<T> ItemRemovedAt + { + add { throw new UnlistenableEventException(); } + remove { throw new UnlistenableEventException(); } + } + + /// <summary> + /// + /// </summary> + /// <value></value> + public bool IsEmpty { get { return innerlist.IsEmpty; } } + + /// <summary> + /// + /// </summary> + /// <value></value> + public int Count { get { return innerlist.Count; } } + + /// <summary> + /// + /// </summary> + /// <value></value> + public Speed CountSpeed { get { return innerlist.CountSpeed; } } + + /// <summary> + /// + /// </summary> + /// <param name="array"></param> + /// <param name="index"></param> + public void CopyTo(T[] array, int index) { innerlist.CopyTo(array, index); } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public T[] ToArray() { return innerlist.ToArray(); } + + /// <summary> + /// + /// </summary> + /// <param name="action"></param> + public void Apply(Act<T> action) { innerlist.Apply(action); } + + /// <summary> + /// + /// </summary> + /// <param name="predicate"></param> + /// <returns></returns> + public bool Exists(Fun<T, bool> predicate) { return innerlist.Exists(predicate); } + + /// <summary> + /// + /// </summary> + /// <param name="predicate"></param> + /// <param name="item"></param> + /// <returns></returns> + public bool Find(Fun<T, bool> predicate, out T item) { return innerlist.Find(predicate, out item); } + + /// <summary> + /// + /// </summary> + /// <param name="predicate"></param> + /// <returns></returns> + public bool All(Fun<T, bool> predicate) { return innerlist.All(predicate); } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public T Choose() { return innerlist.Choose(); } + + /// <summary> + /// + /// </summary> + /// <param name="filter"></param> + /// <returns></returns> + public SCG.IEnumerable<T> Filter(Fun<T, bool> filter) { return innerlist.Filter(filter); } + + #endregion + + #region IEnumerable<T> Members + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public SCG.IEnumerator<T> GetEnumerator() { return innerlist.GetEnumerator(); } + #endregion + + #region IShowable Members + + /// <summary> + /// + /// </summary> + /// <param name="stringbuilder"></param> + /// <param name="rest"></param> + /// <param name="formatProvider"></param> + /// <returns></returns> + public bool Show(StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider) + { return innerlist.Show(stringbuilder, ref rest, formatProvider); } + + #endregion + + #region IFormattable Members + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public override string ToString() { return innerlist.ToString(); } + + + /// <summary> + /// + /// </summary> + /// <param name="format"></param> + /// <param name="formatProvider"></param> + /// <returns></returns> + public virtual string ToString(string format, IFormatProvider formatProvider) { return innerlist.ToString(format, formatProvider); } + + #endregion + + #region IDirectedCollectionValue<T> Members + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public IDirectedCollectionValue<T> Backwards() { return innerlist.Backwards(); } + + /// <summary> + /// + /// </summary> + /// <param name="predicate"></param> + /// <param name="item"></param> + /// <returns></returns> + public bool FindLast(Fun<T, bool> predicate, out T item) { return innerlist.FindLast(predicate, out item); } + + #endregion + + #region IDirectedEnumerable<T> Members + + IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return Backwards(); } + + /// <summary> + /// + /// </summary> + /// <value></value> + public EnumerationDirection Direction { get { return EnumerationDirection.Forwards; } } + + #endregion + + #region IDisposable Members + + /// <summary> + /// Dispose this if a view else operation is illegal + /// </summary> + /// <exception cref="FixedSizeCollectionException">If not a view</exception> + public void Dispose() + { + if (underlying == null) + throw new FixedSizeCollectionException(); + else + innerlist.Dispose(); + } + + #endregion + + #region ICloneable Members + + /// <summary> + /// Make a shallow copy of this WrappedArray. + /// + /// + /// </summary> + /// <returns></returns> + public virtual object Clone() + { + return new WrappedArray<T>(innerlist.ToArray()); + } + + #endregion + + #region System.Collections.Generic.IList<T> Members + + void System.Collections.Generic.IList<T>.RemoveAt(int index) + { + throw new FixedSizeCollectionException(); + } + + void System.Collections.Generic.ICollection<T>.Add(T item) + { + throw new FixedSizeCollectionException(); + } + + #endregion + + #region System.Collections.ICollection Members + + bool System.Collections.ICollection.IsSynchronized + { + get { return false; } + } + + [Obsolete] + Object System.Collections.ICollection.SyncRoot + { + get { return ((System.Collections.IList)innerlist).SyncRoot; } + } + + void System.Collections.ICollection.CopyTo(Array arr, int index) + { + if (index < 0 || index + Count > arr.Length) + throw new ArgumentOutOfRangeException(); + + foreach (T item in this) + arr.SetValue(item, index++); + } + + #endregion + + #region System.Collections.IList Members + + Object System.Collections.IList.this[int index] + { + get { return this[index]; } + set { this[index] = (T)value; } + } + + int System.Collections.IList.Add(Object o) + { + bool added = Add((T)o); + // What position to report if item not added? SC.IList.Add doesn't say + return added ? Count - 1 : -1; + } + + bool System.Collections.IList.Contains(Object o) + { + return Contains((T)o); + } + + int System.Collections.IList.IndexOf(Object o) + { + return Math.Max(-1, IndexOf((T)o)); + } + + void System.Collections.IList.Insert(int index, Object o) + { + Insert(index, (T)o); + } + + void System.Collections.IList.Remove(Object o) + { + Remove((T)o); + } + + void System.Collections.IList.RemoveAt(int index) + { + RemoveAt(index); + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + throw new Exception("The method or operation is not implemented."); + } + + #endregion + } }
\ No newline at end of file diff --git a/mcs/class/Mono.C5/C5/Wrappers.cs b/mcs/class/Mono.C5/C5/Wrappers.cs index 6d1ca0a10d0..5266bce1a8e 100644 --- a/mcs/class/Mono.C5/C5/Wrappers.cs +++ b/mcs/class/Mono.C5/C5/Wrappers.cs @@ -1,2405 +1,2405 @@ -/*
- Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-
-using System;
-using System.Diagnostics;
-using SCG = System.Collections.Generic;
-namespace C5
-{
- /// <summary>
- /// A read-only wrapper class for a generic enumerator
- /// </summary>
- public class GuardedEnumerator<T> : SCG.IEnumerator<T>
- {
- #region Fields
-
- SCG.IEnumerator<T> enumerator;
-
- #endregion
-
- #region Constructor
-
- /// <summary>
- /// Create a wrapper around a generic enumerator
- /// </summary>
- /// <param name="enumerator">The enumerator to wrap</param>
- public GuardedEnumerator(SCG.IEnumerator<T> enumerator)
- { this.enumerator = enumerator; }
-
- #endregion
-
- #region IEnumerator<T> Members
-
- /// <summary>
- /// Move wrapped enumerator to next item, or the first item if
- /// this is the first call to MoveNext.
- /// </summary>
- /// <returns>True if enumerator is valid now</returns>
- public bool MoveNext() { return enumerator.MoveNext(); }
-
-
- /// <summary>
- /// Undefined if enumerator is not valid (MoveNext hash been called returning true)
- /// </summary>
- /// <value>The current item of the wrapped enumerator.</value>
- public T Current { get { return enumerator.Current; } }
-
- #endregion
-
- #region IDisposable Members
-
- //TODO: consider possible danger of calling through to Dispose.
- /// <summary>
- /// Dispose wrapped enumerator.
- /// </summary>
- public void Dispose() { enumerator.Dispose(); }
-
- #endregion
-
-
- #region IEnumerator Members
-
- object System.Collections.IEnumerator.Current
- {
- get { return enumerator.Current; }
- }
-
- void System.Collections.IEnumerator.Reset()
- {
- enumerator.Reset();
- }
-
- #endregion
- }
-
-
-
- /// <summary>
- /// A read-only wrapper class for a generic enumerable
- ///
- /// <i>This is mainly interesting as a base of other guard classes</i>
- /// </summary>
- public class GuardedEnumerable<T> : SCG.IEnumerable<T>
- {
- #region Fields
-
- SCG.IEnumerable<T> enumerable;
-
- #endregion
-
- #region Constructor
-
- /// <summary>
- /// Wrap an enumerable in a read-only wrapper
- /// </summary>
- /// <param name="enumerable">The enumerable to wrap</param>
- public GuardedEnumerable(SCG.IEnumerable<T> enumerable)
- { this.enumerable = enumerable; }
-
- #endregion
-
- #region SCG.IEnumerable<T> Members
-
- /// <summary>
- /// Get an enumerator from the wrapped enumerable
- /// </summary>
- /// <returns>The enumerator (itself wrapped)</returns>
- public SCG.IEnumerator<T> GetEnumerator()
- { return new GuardedEnumerator<T>(enumerable.GetEnumerator()); }
-
- #endregion
-
- #region IEnumerable Members
-
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
-
- #endregion
-
- }
-
-
-
- /// <summary>
- /// A read-only wrapper for a generic directed enumerable
- ///
- /// <i>This is mainly interesting as a base of other guard classes</i>
- /// </summary>
- public class GuardedDirectedEnumerable<T> : GuardedEnumerable<T>, IDirectedEnumerable<T>
- {
- #region Fields
-
- IDirectedEnumerable<T> directedenumerable;
-
- #endregion
-
- #region Constructor
-
- /// <summary>
- /// Wrap a directed enumerable in a read-only wrapper
- /// </summary>
- /// <param name="directedenumerable">the collection to wrap</param>
- public GuardedDirectedEnumerable(IDirectedEnumerable<T> directedenumerable)
- : base(directedenumerable)
- { this.directedenumerable = directedenumerable; }
-
- #endregion
-
- #region IDirectedEnumerable<T> Members
-
- /// <summary>
- /// Get a enumerable that enumerates the wrapped collection in the opposite direction
- /// </summary>
- /// <returns>The mirrored enumerable</returns>
- public IDirectedEnumerable<T> Backwards()
- { return new GuardedDirectedEnumerable<T>(directedenumerable.Backwards()); }
-
-
- /// <summary>
- /// <code>Forwards</code> if same, else <code>Backwards</code>
- /// </summary>
- /// <value>The enumeration direction relative to the original collection.</value>
- public EnumerationDirection Direction
- { get { return directedenumerable.Direction; } }
-
- #endregion
- }
-
-
-
- /// <summary>
- /// A read-only wrapper for an ICollectionValue<T>
- ///
- /// <i>This is mainly interesting as a base of other guard classes</i>
- /// </summary>
- public class GuardedCollectionValue<T> : GuardedEnumerable<T>, ICollectionValue<T>
- {
- #region Events
- /// <summary>
- /// The ListenableEvents value of the wrapped collection
- /// </summary>
- /// <value></value>
- public virtual EventTypeEnum ListenableEvents { get { return collectionvalue.ListenableEvents; } }
-
- /// <summary>
- /// The ActiveEvents value of the wrapped collection
- /// </summary>
- /// <value></value>
- public virtual EventTypeEnum ActiveEvents { get { return collectionvalue.ActiveEvents; } }
-
- ProxyEventBlock<T> eventBlock;
- /// <summary>
- /// The change event. Will be raised for every change operation on the collection.
- /// </summary>
- public event CollectionChangedHandler<T> CollectionChanged
- {
- add { (eventBlock ?? (eventBlock = new ProxyEventBlock<T>(this, collectionvalue))).CollectionChanged += value; }
- remove { if (eventBlock != null) eventBlock.CollectionChanged -= value; }
- }
-
- /// <summary>
- /// The change event. Will be raised for every change operation on the collection.
- /// </summary>
- public event CollectionClearedHandler<T> CollectionCleared
- {
- add { (eventBlock ?? (eventBlock = new ProxyEventBlock<T>(this, collectionvalue))).CollectionCleared += value; }
- remove { if (eventBlock != null) eventBlock.CollectionCleared -= value; }
- }
-
- /// <summary>
- /// The item added event. Will be raised for every individual addition to the collection.
- /// </summary>
- public event ItemsAddedHandler<T> ItemsAdded
- {
- add { (eventBlock ?? (eventBlock = new ProxyEventBlock<T>(this, collectionvalue))).ItemsAdded += value; }
- remove { if (eventBlock != null) eventBlock.ItemsAdded -= value; }
- }
-
- /// <summary>
- /// The item added event. Will be raised for every individual addition to the collection.
- /// </summary>
- public event ItemInsertedHandler<T> ItemInserted
- {
- add { (eventBlock ?? (eventBlock = new ProxyEventBlock<T>(this, collectionvalue))).ItemInserted += value; }
- remove { if (eventBlock != null) eventBlock.ItemInserted -= value; }
- }
-
- /// <summary>
- /// The item removed event. Will be raised for every individual removal from the collection.
- /// </summary>
- public event ItemsRemovedHandler<T> ItemsRemoved
- {
- add { (eventBlock ?? (eventBlock = new ProxyEventBlock<T>(this, collectionvalue))).ItemsRemoved += value; }
- remove { if (eventBlock != null) eventBlock.ItemsRemoved -= value; }
- }
-
- /// <summary>
- /// The item removed event. Will be raised for every individual removal from the collection.
- /// </summary>
- public event ItemRemovedAtHandler<T> ItemRemovedAt
- {
- add { (eventBlock ?? (eventBlock = new ProxyEventBlock<T>(this, collectionvalue))).ItemRemovedAt += value; }
- remove { if (eventBlock != null) eventBlock.ItemRemovedAt -= value; }
- }
- #endregion
-
- #region Fields
-
- ICollectionValue<T> collectionvalue;
-
- #endregion
-
- #region Constructor
-
- /// <summary>
- /// Wrap a ICollectionValue<T> in a read-only wrapper
- /// </summary>
- /// <param name="collectionvalue">the collection to wrap</param>
- public GuardedCollectionValue(ICollectionValue<T> collectionvalue)
- : base(collectionvalue)
- { this.collectionvalue = collectionvalue; }
-
- #endregion
-
- #region ICollection<T> Members
-
- /// <summary>
- /// Get the size of the wrapped collection
- /// </summary>
- /// <value>The size</value>
- public virtual bool IsEmpty { get { return collectionvalue.IsEmpty; } }
-
- /// <summary>
- /// Get the size of the wrapped collection
- /// </summary>
- /// <value>The size</value>
- public virtual int Count { get { return collectionvalue.Count; } }
-
- /// <summary>
- /// The value is symbolic indicating the type of asymptotic complexity
- /// in terms of the size of this collection (worst-case or amortized as
- /// relevant).
- /// </summary>
- /// <value>A characterization of the speed of the
- /// <code>Count</code> property in this collection.</value>
- public virtual Speed CountSpeed { get { return collectionvalue.CountSpeed; } }
-
- /// <summary>
- /// Copy the items of the wrapped collection to an array
- /// </summary>
- /// <param name="a">The array</param>
- /// <param name="i">Starting offset</param>
- public virtual void CopyTo(T[] a, int i) { collectionvalue.CopyTo(a, i); }
-
- /// <summary>
- /// Create an array from the items of the wrapped collection
- /// </summary>
- /// <returns>The array</returns>
- public virtual T[] ToArray() { return collectionvalue.ToArray(); }
-
- /// <summary>
- /// Apply a delegate to all items of the wrapped enumerable.
- /// </summary>
- /// <param name="a">The delegate to apply</param>
- //TODO: change this to throw an exception?
- public virtual void Apply(Act<T> a) { collectionvalue.Apply(a); }
-
-
- /// <summary>
- /// Check if there exists an item that satisfies a
- /// specific predicate in the wrapped enumerable.
- /// </summary>
- /// <param name="filter">A filter delegate
- /// (<see cref="T:C5.Filter`1"/>) defining the predicate</param>
- /// <returns>True is such an item exists</returns>
- public virtual bool Exists(Fun<T, bool> filter) { return collectionvalue.Exists(filter); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="filter"></param>
- /// <param name="item"></param>
- /// <returns></returns>
- public virtual bool Find(Fun<T, bool> filter, out T item) { return collectionvalue.Find(filter, out item); }
-
- /// <summary>
- /// Check if all items in the wrapped enumerable satisfies a specific predicate.
- /// </summary>
- /// <param name="filter">A filter delegate
- /// (<see cref="T:C5.Filter`1"/>) defining the predicate</param>
- /// <returns>True if all items satisfies the predicate</returns>
- public virtual bool All(Fun<T, bool> filter) { return collectionvalue.All(filter); }
-
- /// <summary>
- /// Create an enumerable, enumerating the items of this collection that satisfies
- /// a certain condition.
- /// </summary>
- /// <param name="filter">The T->bool filter delegate defining the condition</param>
- /// <returns>The filtered enumerable</returns>
- public virtual SCG.IEnumerable<T> Filter(Fun<T, bool> filter) { return collectionvalue.Filter(filter); }
-
- /// <summary>
- /// Choose some item of this collection.
- /// </summary>
- /// <exception cref="NoSuchItemException">if collection is empty.</exception>
- /// <returns></returns>
- public virtual T Choose() { return collectionvalue.Choose(); }
-
- #endregion
-
- #region IShowable Members
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="stringbuilder"></param>
- /// <param name="formatProvider"></param>
- /// <param name="rest"></param>
- /// <returns></returns>
- public bool Show(System.Text.StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider)
- {
- return collectionvalue.Show(stringbuilder, ref rest, formatProvider);
- }
- #endregion
-
- #region IFormattable Members
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="format"></param>
- /// <param name="formatProvider"></param>
- /// <returns></returns>
- public string ToString(string format, IFormatProvider formatProvider)
- {
- return collectionvalue.ToString(format, formatProvider);
- }
-
- #endregion
- }
-
-
-
- /// <summary>
- /// A read-only wrapper for a directed collection
- ///
- /// <i>This is mainly interesting as a base of other guard classes</i>
- /// </summary>
- public class GuardedDirectedCollectionValue<T> : GuardedCollectionValue<T>, IDirectedCollectionValue<T>
- {
- #region Fields
-
- IDirectedCollectionValue<T> directedcollection;
-
- #endregion
-
- #region Constructor
-
- /// <summary>
- /// Wrap a directed collection in a read-only wrapper
- /// </summary>
- /// <param name="directedcollection">the collection to wrap</param>
- public GuardedDirectedCollectionValue(IDirectedCollectionValue<T> directedcollection)
- :
- base(directedcollection)
- { this.directedcollection = directedcollection; }
-
- #endregion
-
- #region IDirectedCollection<T> Members
-
- /// <summary>
- /// Get a collection that enumerates the wrapped collection in the opposite direction
- /// </summary>
- /// <returns>The mirrored collection</returns>
- public virtual IDirectedCollectionValue<T> Backwards()
- { return new GuardedDirectedCollectionValue<T>(directedcollection.Backwards()); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="predicate"></param>
- /// <param name="item"></param>
- /// <returns></returns>
- public virtual bool FindLast(Fun<T, bool> predicate, out T item) { return directedcollection.FindLast(predicate, out item); }
-
- #endregion
-
- #region IDirectedEnumerable<T> Members
-
- IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards()
- { return Backwards(); }
-
-
- /// <summary>
- /// <code>Forwards</code> if same, else <code>Backwards</code>
- /// </summary>
- /// <value>The enumeration direction relative to the original collection.</value>
- public EnumerationDirection Direction
- { get { return directedcollection.Direction; } }
-
- #endregion
- }
-
-
-
- /// <summary>
- /// A read-only wrapper for an <see cref="T:C5.ICollection`1"/>,
- /// <para>
- /// <i>Suitable for wrapping hash tables, <see cref="T:C5.HashSet`1"/>
- /// and <see cref="T:C5.HashBag`1"/> </i></para>
- /// </summary>
- public class GuardedCollection<T> : GuardedCollectionValue<T>, ICollection<T>
- {
- #region Fields
-
- ICollection<T> collection;
-
- #endregion
-
- #region Constructor
-
- /// <summary>
- /// Wrap an ICollection<T> in a read-only wrapper
- /// </summary>
- /// <param name="collection">the collection to wrap</param>
- public GuardedCollection(ICollection<T> collection)
- : base(collection)
- {
- this.collection = collection;
- }
-
- #endregion
-
- #region ICollection<T> Members
-
- /// <summary>
- /// (This is a read-only wrapper)
- /// </summary>
- /// <value>True</value>
- public virtual bool IsReadOnly { get { return true; } }
-
-
- /// <summary> </summary>
- /// <value>Speed of wrapped collection</value>
- public virtual Speed ContainsSpeed { get { return collection.ContainsSpeed; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public virtual int GetUnsequencedHashCode()
- { return collection.GetUnsequencedHashCode(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="that"></param>
- /// <returns></returns>
- public virtual bool UnsequencedEquals(ICollection<T> that)
- { return collection.UnsequencedEquals(that); }
-
-
- /// <summary>
- /// Check if an item is in the wrapped collection
- /// </summary>
- /// <param name="item">The item</param>
- /// <returns>True if found</returns>
- public virtual bool Contains(T item) { return collection.Contains(item); }
-
-
- /// <summary>
- /// Count the number of times an item appears in the wrapped collection
- /// </summary>
- /// <param name="item">The item</param>
- /// <returns>The number of copies</returns>
- public virtual int ContainsCount(T item) { return collection.ContainsCount(item); }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public virtual ICollectionValue<T> UniqueItems() { return new GuardedCollectionValue<T>(collection.UniqueItems()); }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public virtual ICollectionValue<KeyValuePair<T, int>> ItemMultiplicities() { return new GuardedCollectionValue<KeyValuePair<T, int>>(collection.ItemMultiplicities()); }
-
- /// <summary>
- /// Check if all items in the argument is in the wrapped collection
- /// </summary>
- /// <param name="items">The items</param>
- /// <typeparam name="U"></typeparam>
- /// <returns>True if so</returns>
- public virtual bool ContainsAll<U>(SCG.IEnumerable<U> items) where U : T { return collection.ContainsAll(items); }
-
- /// <summary>
- /// Search for an item in the wrapped collection
- /// </summary>
- /// <param name="item">On entry the item to look for, on exit the equivalent item found (if any)</param>
- /// <returns></returns>
- public virtual bool Find(ref T item) { return collection.Find(ref item); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="item"></param>
- /// <returns></returns>
- public virtual bool FindOrAdd(ref T item)
- { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="item"></param>
- /// <returns></returns>
- public virtual bool Update(T item)
- { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="item"></param>
- /// <param name="olditem"></param>
- /// <returns></returns>
- public virtual bool Update(T item, out T olditem)
- { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="item"></param>
- /// <returns></returns>
- public virtual bool UpdateOrAdd(T item)
- { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="item"></param>
- /// <param name="olditem"></param>
- /// <returns></returns>
- public virtual bool UpdateOrAdd(T item, out T olditem)
- { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); }
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="item"></param>
- /// <returns></returns>
- public virtual bool Remove(T item)
- { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="item">The value to remove.</param>
- /// <param name="removeditem">The removed value.</param>
- /// <returns></returns>
- public virtual bool Remove(T item, out T removeditem)
- { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="item"></param>
- public virtual void RemoveAllCopies(T item)
- { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <typeparam name="U"></typeparam>
- /// <param name="items"></param>
- public virtual void RemoveAll<U>(SCG.IEnumerable<U> items) where U : T
- { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); }
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- public virtual void Clear()
- { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <typeparam name="U"></typeparam>
- /// <param name="items"></param>
- public virtual void RetainAll<U>(SCG.IEnumerable<U> items) where U : T
- { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); }
-
- /// <summary>
- /// Check wrapped collection for internal consistency
- /// </summary>
- /// <returns>True if check passed</returns>
- public virtual bool Check() { return collection.Check(); }
-
- #endregion
-
- #region IExtensible<T> Members
-
- /// <summary> </summary>
- /// <value>False if wrapped collection has set semantics</value>
- public virtual bool AllowsDuplicates { get { return collection.AllowsDuplicates; } }
-
- //TODO: the equalityComparer should be guarded
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public virtual SCG.IEqualityComparer<T> EqualityComparer { get { return collection.EqualityComparer; } }
-
- /// <summary>
- /// By convention this is true for any collection with set semantics.
- /// </summary>
- /// <value>True if only one representative of a group of equal items
- /// is kept in the collection together with the total count.</value>
- public virtual bool DuplicatesByCounting { get { return collection.DuplicatesByCounting; } }
-
-
- /// <summary> </summary>
- /// <value>True if wrapped collection is empty</value>
- public override bool IsEmpty { get { return collection.IsEmpty; } }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="item"></param>
- /// <returns></returns>
- public virtual bool Add(T item)
- { throw new ReadOnlyCollectionException(); }
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="item"></param>
- void SCG.ICollection<T>.Add(T item)
- { throw new ReadOnlyCollectionException(); }
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <typeparam name="U"></typeparam>
- /// <param name="items"></param>
- public virtual void AddAll<U>(SCG.IEnumerable<U> items) where U : T
- { throw new ReadOnlyCollectionException(); }
-
- #endregion
-
- #region ICloneable Members
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public virtual object Clone()
- {
- return new GuardedCollection<T>((ICollection<T>)(collection.Clone()));
- }
-
- #endregion
-
- }
-
-
- /// <summary>
- /// A read-only wrapper for a sequenced collection
- ///
- /// <i>This is mainly interesting as a base of other guard classes</i>
- /// </summary>
- public class GuardedSequenced<T> : GuardedCollection<T>, ISequenced<T>
- {
- #region Fields
-
- ISequenced<T> sequenced;
-
- #endregion
-
- #region Constructor
-
- /// <summary>
- /// Wrap a sequenced collection in a read-only wrapper
- /// </summary>
- /// <param name="sorted"></param>
- public GuardedSequenced(ISequenced<T> sorted) : base(sorted) { this.sequenced = sorted; }
-
- #endregion
-
- /// <summary>
- /// Check if there exists an item that satisfies a
- /// specific predicate in this collection and return the index of the first one.
- /// </summary>
- /// <param name="predicate">A delegate
- /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param>
- /// <returns>the index, if found, a negative value else</returns>
- public int FindIndex(Fun<T, bool> predicate)
- {
- IIndexed<T> indexed = sequenced as IIndexed<T>;
- if (indexed != null)
- return indexed.FindIndex(predicate);
- int index = 0;
- foreach (T item in this)
- {
- if (predicate(item))
- return index;
- index++;
- }
- return -1;
- }
-
- /// <summary>
- /// Check if there exists an item that satisfies a
- /// specific predicate in this collection and return the index of the last one.
- /// </summary>
- /// <param name="predicate">A delegate
- /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param>
- /// <returns>the index, if found, a negative value else</returns>
- public int FindLastIndex(Fun<T, bool> predicate)
- {
- IIndexed<T> indexed = sequenced as IIndexed<T>;
- if (indexed != null)
- return indexed.FindLastIndex(predicate);
- int index = Count - 1;
- foreach (T item in Backwards())
- {
- if (predicate(item))
- return index;
- index--;
- }
- return -1;
- }
-
-
-
- #region ISequenced<T> Members
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public int GetSequencedHashCode()
- { return sequenced.GetSequencedHashCode(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="that"></param>
- /// <returns></returns>
- public bool SequencedEquals(ISequenced<T> that)
- { return sequenced.SequencedEquals(that); }
-
- #endregion
-
- #region IDirectedCollection<T> Members
-
- /// <summary>
- /// Get a collection that enumerates the wrapped collection in the opposite direction
- /// </summary>
- /// <returns>The mirrored collection</returns>
- public virtual IDirectedCollectionValue<T> Backwards()
- { return new GuardedDirectedCollectionValue<T>(sequenced.Backwards()); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="predicate"></param>
- /// <param name="item"></param>
- /// <returns></returns>
- public virtual bool FindLast(Fun<T, bool> predicate, out T item) { return sequenced.FindLast(predicate, out item); }
-
- #endregion
-
- #region IDirectedEnumerable<T> Members
-
- IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards()
- { return Backwards(); }
-
-
-
- /// <summary>
- /// <code>Forwards</code> if same, else <code>Backwards</code>
- /// </summary>
- /// <value>The enumeration direction relative to the original collection.</value>
- public EnumerationDirection Direction
- { get { return EnumerationDirection.Forwards; } }
-
- #endregion
-
- #region ICloneable Members
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public override object Clone()
- {
- return new GuardedCollection<T>((ISequenced<T>)(sequenced.Clone()));
- }
-
- #endregion
-
- }
-
-
- /// <summary>
- /// A read-only wrapper for a sorted collection
- ///
- /// <i>This is mainly interesting as a base of other guard classes</i>
- /// </summary>
- public class GuardedSorted<T> : GuardedSequenced<T>, ISorted<T>
- {
- #region Fields
-
- ISorted<T> sorted;
-
- #endregion
-
- #region Constructor
-
- /// <summary>
- /// Wrap a sorted collection in a read-only wrapper
- /// </summary>
- /// <param name="sorted"></param>
- public GuardedSorted(ISorted<T> sorted) : base(sorted) { this.sorted = sorted; }
-
- #endregion
-
- #region ISorted<T> Members
-
- /// <summary>
- /// Find the strict predecessor of item in the guarded sorted collection,
- /// that is, the greatest item in the collection smaller than the item.
- /// </summary>
- /// <param name="item">The item to find the predecessor for.</param>
- /// <param name="res">The predecessor, if any; otherwise the default value for T.</param>
- /// <returns>True if item has a predecessor; otherwise false.</returns>
- public bool TryPredecessor(T item, out T res) { return sorted.TryPredecessor(item, out res); }
-
-
- /// <summary>
- /// Find the strict successor of item in the guarded sorted collection,
- /// that is, the least item in the collection greater than the supplied value.
- /// </summary>
- /// <param name="item">The item to find the successor for.</param>
- /// <param name="res">The successor, if any; otherwise the default value for T.</param>
- /// <returns>True if item has a successor; otherwise false.</returns>
- public bool TrySuccessor(T item, out T res) { return sorted.TrySuccessor(item, out res); }
-
-
- /// <summary>
- /// Find the weak predecessor of item in the guarded sorted collection,
- /// that is, the greatest item in the collection smaller than or equal to the item.
- /// </summary>
- /// <param name="item">The item to find the weak predecessor for.</param>
- /// <param name="res">The weak predecessor, if any; otherwise the default value for T.</param>
- /// <returns>True if item has a weak predecessor; otherwise false.</returns>
- public bool TryWeakPredecessor(T item, out T res) { return sorted.TryWeakPredecessor(item, out res); }
-
-
- /// <summary>
- /// Find the weak successor of item in the sorted collection,
- /// that is, the least item in the collection greater than or equal to the supplied value.
- /// </summary>
- /// <param name="item">The item to find the weak successor for.</param>
- /// <param name="res">The weak successor, if any; otherwise the default value for T.</param>
- /// <returns>True if item has a weak successor; otherwise false.</returns>
- public bool TryWeakSuccessor(T item, out T res) { return sorted.TryWeakSuccessor(item, out res); }
-
-
- /// <summary>
- /// Find the predecessor of the item in the wrapped sorted collection
- /// </summary>
- /// <exception cref="NoSuchItemException"> if no such element exists </exception>
- /// <param name="item">The item</param>
- /// <returns>The predecessor</returns>
- public T Predecessor(T item) { return sorted.Predecessor(item); }
-
-
- /// <summary>
- /// Find the Successor of the item in the wrapped sorted collection
- /// </summary>
- /// <exception cref="NoSuchItemException"> if no such element exists </exception>
- /// <param name="item">The item</param>
- /// <returns>The Successor</returns>
- public T Successor(T item) { return sorted.Successor(item); }
-
-
- /// <summary>
- /// Find the weak predecessor of the item in the wrapped sorted collection
- /// </summary>
- /// <exception cref="NoSuchItemException"> if no such element exists </exception>
- /// <param name="item">The item</param>
- /// <returns>The weak predecessor</returns>
- public T WeakPredecessor(T item) { return sorted.WeakPredecessor(item); }
-
-
- /// <summary>
- /// Find the weak Successor of the item in the wrapped sorted collection
- /// </summary>
- /// <exception cref="NoSuchItemException"> if no such element exists </exception>
- /// <param name="item">The item</param>
- /// <returns>The weak Successor</returns>
- public T WeakSuccessor(T item) { return sorted.WeakSuccessor(item); }
-
-
- /// <summary>
- /// Run Cut on the wrapped sorted collection
- /// </summary>
- /// <param name="c"></param>
- /// <param name="low"></param>
- /// <param name="lval"></param>
- /// <param name="high"></param>
- /// <param name="hval"></param>
- /// <returns></returns>
- public bool Cut(IComparable<T> c, out T low, out bool lval, out T high, out bool hval)
- { return sorted.Cut(c, out low, out lval, out high, out hval); }
-
-
- /// <summary>
- /// Get the specified range from the wrapped collection.
- /// (The current implementation erroneously does not wrap the result.)
- /// </summary>
- /// <param name="bot"></param>
- /// <returns></returns>
- public IDirectedEnumerable<T> RangeFrom(T bot) { return sorted.RangeFrom(bot); }
-
-
- /// <summary>
- /// Get the specified range from the wrapped collection.
- /// (The current implementation erroneously does not wrap the result.)
- /// </summary>
- /// <param name="bot"></param>
- /// <param name="top"></param>
- /// <returns></returns>
- public IDirectedEnumerable<T> RangeFromTo(T bot, T top)
- { return sorted.RangeFromTo(bot, top); }
-
-
- /// <summary>
- /// Get the specified range from the wrapped collection.
- /// (The current implementation erroneously does not wrap the result.)
- /// </summary>
- /// <param name="top"></param>
- /// <returns></returns>
- public IDirectedEnumerable<T> RangeTo(T top) { return sorted.RangeTo(top); }
-
-
- /// <summary>
- /// Get the specified range from the wrapped collection.
- /// (The current implementation erroneously does not wrap the result.)
- /// </summary>
- /// <returns></returns>
- public IDirectedCollectionValue<T> RangeAll() { return sorted.RangeAll(); }
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="items"></param>
- /// <typeparam name="U"></typeparam>
- public void AddSorted<U>(SCG.IEnumerable<U> items) where U : T
- { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); }
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="low"></param>
- public void RemoveRangeFrom(T low)
- { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="low"></param>
- /// <param name="hi"></param>
- public void RemoveRangeFromTo(T low, T hi)
- { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="hi"></param>
- public void RemoveRangeTo(T hi)
- { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); }
-
- #endregion
-
- #region IPriorityQueue<T> Members
-
- /// <summary>
- /// Find the minimum of the wrapped collection
- /// </summary>
- /// <returns>The minimum</returns>
- public T FindMin() { return sorted.FindMin(); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <returns></returns>
- public T DeleteMin()
- { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); }
-
-
- /// <summary>
- /// Find the maximum of the wrapped collection
- /// </summary>
- /// <returns>The maximum</returns>
- public T FindMax() { return sorted.FindMax(); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <returns></returns>
- public T DeleteMax()
- { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); }
-
- //TODO: we should guard the comparer!
- /// <summary>
- /// The comparer object supplied at creation time for the underlying collection
- /// </summary>
- /// <value>The comparer</value>
- public SCG.IComparer<T> Comparer { get { return sorted.Comparer; } }
- #endregion
-
- #region IDirectedEnumerable<T> Members
-
- IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards()
- { return Backwards(); }
-
- #endregion
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public override object Clone()
- {
- return new GuardedSorted<T>((ISorted<T>)(sorted.Clone()));
- }
-
- }
-
-
-
- /// <summary>
- /// Read-only wrapper for indexed sorted collections
- ///
- /// <i>Suitable for wrapping TreeSet, TreeBag and SortedArray</i>
- /// </summary>
- public class GuardedIndexedSorted<T> : GuardedSorted<T>, IIndexedSorted<T>
- {
- #region Fields
-
- IIndexedSorted<T> indexedsorted;
-
- #endregion
-
- #region Constructor
-
- /// <summary>
- /// Wrap an indexed sorted collection in a read-only wrapper
- /// </summary>
- /// <param name="list">the indexed sorted collection</param>
- public GuardedIndexedSorted(IIndexedSorted<T> list)
- : base(list)
- { this.indexedsorted = list; }
-
- #endregion
-
- #region IIndexedSorted<T> Members
-
- /// <summary>
- /// Get the specified range from the wrapped collection.
- /// (The current implementation erroneously does not wrap the result.)
- /// </summary>
- /// <param name="bot"></param>
- /// <returns></returns>
- public new IDirectedCollectionValue<T> RangeFrom(T bot)
- { return indexedsorted.RangeFrom(bot); }
-
-
- /// <summary>
- /// Get the specified range from the wrapped collection.
- /// (The current implementation erroneously does not wrap the result.)
- /// </summary>
- /// <param name="bot"></param>
- /// <param name="top"></param>
- /// <returns></returns>
- public new IDirectedCollectionValue<T> RangeFromTo(T bot, T top)
- { return indexedsorted.RangeFromTo(bot, top); }
-
-
- /// <summary>
- /// Get the specified range from the wrapped collection.
- /// (The current implementation erroneously does not wrap the result.)
- /// </summary>
- /// <param name="top"></param>
- /// <returns></returns>
- public new IDirectedCollectionValue<T> RangeTo(T top)
- { return indexedsorted.RangeTo(top); }
-
-
- /// <summary>
- /// Report the number of items in the specified range of the wrapped collection
- /// </summary>
- /// <param name="bot"></param>
- /// <returns></returns>
- public int CountFrom(T bot) { return indexedsorted.CountFrom(bot); }
-
-
- /// <summary>
- /// Report the number of items in the specified range of the wrapped collection
- /// </summary>
- /// <param name="bot"></param>
- /// <param name="top"></param>
- /// <returns></returns>
- public int CountFromTo(T bot, T top) { return indexedsorted.CountFromTo(bot, top); }
-
-
- /// <summary>
- /// Report the number of items in the specified range of the wrapped collection
- /// </summary>
- /// <param name="top"></param>
- /// <returns></returns>
- public int CountTo(T top) { return indexedsorted.CountTo(top); }
-
-
- /// <summary>
- /// Run FindAll on the wrapped collection with the indicated filter.
- /// The result will <b>not</b> be read-only.
- /// </summary>
- /// <param name="f"></param>
- /// <returns></returns>
- public IIndexedSorted<T> FindAll(Fun<T, bool> f)
- { return indexedsorted.FindAll(f); }
-
-
- /// <summary>
- /// Run Map on the wrapped collection with the indicated mapper.
- /// The result will <b>not</b> be read-only.
- /// </summary>
- /// <param name="m"></param>
- /// <param name="c">The comparer to use in the result</param>
- /// <returns></returns>
- public IIndexedSorted<V> Map<V>(Fun<T, V> m, SCG.IComparer<V> c)
- { return indexedsorted.Map(m, c); }
-
- #endregion
-
- #region IIndexed<T> Members
-
- /// <summary>
- ///
- /// </summary>
- /// <value>The i'th item of the wrapped sorted collection</value>
- public T this[int i] { get { return indexedsorted[i]; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public virtual Speed IndexingSpeed { get { return indexedsorted.IndexingSpeed; } }
-
- /// <summary> </summary>
- /// <value>A directed collection of the items in the indicated interval of the wrapped collection</value>
- public IDirectedCollectionValue<T> this[int start, int end]
- { get { return new GuardedDirectedCollectionValue<T>(indexedsorted[start, end]); } }
-
-
- /// <summary>
- /// Find the (first) index of an item in the wrapped collection
- /// </summary>
- /// <param name="item"></param>
- /// <returns></returns>
- public int IndexOf(T item) { return indexedsorted.IndexOf(item); }
-
-
- /// <summary>
- /// Find the last index of an item in the wrapped collection
- /// </summary>
- /// <param name="item"></param>
- /// <returns></returns>
- public int LastIndexOf(T item) { return indexedsorted.LastIndexOf(item); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="i"></param>
- /// <returns></returns>
- public T RemoveAt(int i)
- { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="start"></param>
- /// <param name="count"></param>
- public void RemoveInterval(int start, int count)
- { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); }
-
- #endregion
-
- #region IDirectedEnumerable<T> Members
-
- IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards()
- { return Backwards(); }
-
- #endregion
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public override object Clone()
- {
- return new GuardedIndexedSorted<T>((IIndexedSorted<T>)(indexedsorted.Clone()));
- }
-
- }
-
-
-
- /// <summary>
- /// A read-only wrapper for a generic list collection
- /// <i>Suitable as a wrapper for LinkedList, HashedLinkedList, ArrayList and HashedArray.
- /// <see cref="T:C5.LinkedList`1"/>,
- /// <see cref="T:C5.HashedLinkedList`1"/>,
- /// <see cref="T:C5.ArrayList`1"/> or
- /// <see cref="T:C5.HashedArray`1"/>.
- /// </i>
- /// </summary>
- public class GuardedList<T> : GuardedSequenced<T>, IList<T>, SCG.IList<T>
- {
- #region Fields
-
- IList<T> innerlist;
- GuardedList<T> underlying;
- bool slidableView = false;
-
- #endregion
-
- #region Constructor
-
- /// <summary>
- /// Wrap a list in a read-only wrapper. A list gets wrapped as read-only,
- /// a list view gets wrapped as read-only and non-slidable.
- /// </summary>
- /// <param name="list">The list</param>
- public GuardedList(IList<T> list)
- : base(list)
- {
- this.innerlist = list;
- // If wrapping a list view, make innerlist = the view, and make
- // underlying = a guarded version of the view's underlying list
- if (list.Underlying != null)
- underlying = new GuardedList<T>(list.Underlying, null, false);
- }
-
- GuardedList(IList<T> list, GuardedList<T> underlying, bool slidableView)
- : base(list)
- {
- this.innerlist = list; this.underlying = underlying; this.slidableView = slidableView;
- }
- #endregion
-
- #region IList<T> Members
-
- /// <summary>
- ///
- /// </summary>
- /// <value>The first item of the wrapped list</value>
- public T First { get { return innerlist.First; } }
-
-
- /// <summary>
- ///
- /// </summary>
- /// <value>The last item of the wrapped list</value>
- public T Last { get { return innerlist.Last; } }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> if used as setter</exception>
- /// <value>True if wrapped list has FIFO semantics for the Add(T item) and Remove() methods</value>
- public bool FIFO
- {
- get { return innerlist.FIFO; }
- set { throw new ReadOnlyCollectionException("List is read only"); }
- }
-
- /// <summary>
- ///
- /// </summary>
- public virtual bool IsFixedSize
- {
- get { return true; }
- }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> if used as setter</exception>
- /// <value>The i'th item of the wrapped list</value>
- public T this[int i]
- {
- get { return innerlist[i]; }
- set { throw new ReadOnlyCollectionException("List is read only"); }
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public virtual Speed IndexingSpeed { get { return innerlist.IndexingSpeed; } }
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="index"></param>
- /// <param name="item"></param>
- public void Insert(int index, T item)
- { throw new ReadOnlyCollectionException(); }
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="pointer"></param>
- /// <param name="item"></param>
- public void Insert(IList<T> pointer, T item)
- { throw new ReadOnlyCollectionException(); }
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="item"></param>
- public void InsertFirst(T item)
- { throw new ReadOnlyCollectionException("List is read only"); }
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="item"></param>
- public void InsertLast(T item)
- { throw new ReadOnlyCollectionException("List is read only"); }
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="item"></param>
- /// <param name="target"></param>
- public void InsertBefore(T item, T target)
- { throw new ReadOnlyCollectionException("List is read only"); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="item"></param>
- /// <param name="target"></param>
- public void InsertAfter(T item, T target)
- { throw new ReadOnlyCollectionException("List is read only"); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="i"></param>
- /// <param name="items"></param>
- public void InsertAll<U>(int i, SCG.IEnumerable<U> items) where U : T
- { throw new ReadOnlyCollectionException("List is read only"); }
-
-
- /// <summary>
- /// Perform FindAll on the wrapped list. The result is <b>not</b> necessarily read-only.
- /// </summary>
- /// <param name="filter">The filter to use</param>
- /// <returns></returns>
- public IList<T> FindAll(Fun<T, bool> filter) { return innerlist.FindAll(filter); }
-
-
- /// <summary>
- /// Perform Map on the wrapped list. The result is <b>not</b> necessarily read-only.
- /// </summary>
- /// <typeparam name="V">The type of items of the new list</typeparam>
- /// <param name="mapper">The mapper to use.</param>
- /// <returns>The mapped list</returns>
- public IList<V> Map<V>(Fun<T, V> mapper) { return innerlist.Map(mapper); }
-
- /// <summary>
- /// Perform Map on the wrapped list. The result is <b>not</b> necessarily read-only.
- /// </summary>
- /// <typeparam name="V">The type of items of the new list</typeparam>
- /// <param name="mapper">The delegate defining the map.</param>
- /// <param name="itemequalityComparer">The itemequalityComparer to use for the new list</param>
- /// <returns>The new list.</returns>
- public IList<V> Map<V>(Fun<T, V> mapper, SCG.IEqualityComparer<V> itemequalityComparer) { return innerlist.Map(mapper, itemequalityComparer); }
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <returns></returns>
- public T Remove() { throw new ReadOnlyCollectionException("List is read only"); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <returns></returns>
- public T RemoveFirst() { throw new ReadOnlyCollectionException("List is read only"); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <returns></returns>
- public T RemoveLast() { throw new ReadOnlyCollectionException("List is read only"); }
-
-
- /// <summary>
- /// Create the indicated view on the wrapped list and wrap it read-only.
- /// </summary>
- /// <param name="start"></param>
- /// <param name="count"></param>
- /// <returns></returns>
- public IList<T> View(int start, int count)
- {
- IList<T> view = innerlist.View(start, count);
- return view == null ? null : new GuardedList<T>(view, underlying ?? this, true);
- }
-
- /// <summary>
- /// Create the indicated view on the wrapped list and wrap it read-only.
- /// </summary>
- /// <param name="item"></param>
- /// <returns></returns>
- public IList<T> ViewOf(T item)
- {
- IList<T> view = innerlist.ViewOf(item);
- return view == null ? null : new GuardedList<T>(view, underlying ?? this, true);
- }
-
- /// <summary>
- /// Create the indicated view on the wrapped list and wrap it read-only.
- /// </summary>
- /// <param name="item"></param>
- /// <returns></returns>
- public IList<T> LastViewOf(T item)
- {
- IList<T> view = innerlist.LastViewOf(item);
- return view == null ? null : new GuardedList<T>(view, underlying ?? this, true);
- }
-
-
- /// <summary>
- /// </summary>
- /// <value>The wrapped underlying list of the wrapped view </value>
- public IList<T> Underlying { get { return underlying; } }
-
-
- /// <summary>
- ///
- /// </summary>
- /// <value>The offset of the wrapped list as a view.</value>
- public int Offset { get { return innerlist.Offset; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public virtual bool IsValid { get { return innerlist.IsValid; } }
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> if this is a wrapped view and not a view that was made on a wrapper</exception>
- /// <param name="offset"></param>
- public IList<T> Slide(int offset)
- {
- if (slidableView)
- {
- innerlist.Slide(offset);
- return this;
- }
- else
- throw new ReadOnlyCollectionException("List is read only");
- }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="offset"></param>
- /// <param name="size"></param>
- public IList<T> Slide(int offset, int size)
- {
- if (slidableView)
- {
- innerlist.Slide(offset, size);
- return this;
- }
- else
- throw new ReadOnlyCollectionException("List is read only");
- }
-
-
- /// <summary>
- ///
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="offset"></param>
- /// <returns></returns>
- public bool TrySlide(int offset)
- {
- if (slidableView)
- return innerlist.TrySlide(offset);
- else
- throw new ReadOnlyCollectionException("List is read only");
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="offset"></param>
- /// <param name="size"></param>
- /// <returns></returns>
- public bool TrySlide(int offset, int size)
- {
- if (slidableView)
- return innerlist.TrySlide(offset, size);
- else
- throw new ReadOnlyCollectionException("List is read only");
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="otherView"></param>
- /// <returns></returns>
- public IList<T> Span(IList<T> otherView)
- {
- GuardedList<T> otherGuardedList = otherView as GuardedList<T>;
- if (otherGuardedList == null)
- throw new IncompatibleViewException();
- IList<T> span = innerlist.Span(otherGuardedList.innerlist);
- if (span == null)
- return null;
- return new GuardedList<T>(span, underlying ?? otherGuardedList.underlying ?? this, true);
- }
-
- /// <summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// </summary>
- public void Reverse() { throw new ReadOnlyCollectionException("List is read only"); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="start"></param>
- /// <param name="count"></param>
- public void Reverse(int start, int count)
- { throw new ReadOnlyCollectionException("List is read only"); }
-
-
- /// <summary>
- /// Check if wrapped list is sorted according to the default sorting order
- /// for the item type T, as defined by the <see cref="T:C5.Comparer`1"/> class
- /// </summary>
- /// <exception cref="NotComparableException">if T is not comparable</exception>
- /// <returns>True if the list is sorted, else false.</returns>
- public bool IsSorted() { return innerlist.IsSorted(Comparer<T>.Default); }
-
- /// <summary>
- /// Check if wrapped list is sorted
- /// </summary>
- /// <param name="c">The sorting order to use</param>
- /// <returns>True if sorted</returns>
- public bool IsSorted(SCG.IComparer<T> c) { return innerlist.IsSorted(c); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- public void Sort()
- { throw new ReadOnlyCollectionException("List is read only"); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="c"></param>
- public void Sort(SCG.IComparer<T> c)
- { throw new ReadOnlyCollectionException("List is read only"); }
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- public void Shuffle()
- { throw new ReadOnlyCollectionException("List is read only"); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="rnd"></param>
- public void Shuffle(Random rnd)
- { throw new ReadOnlyCollectionException("List is read only"); }
-
- #endregion
-
- #region IIndexed<T> Members
-
- /// <summary> </summary>
- /// <value>A directed collection of the items in the indicated interval of the wrapped collection</value>
- public IDirectedCollectionValue<T> this[int start, int end]
- { get { return new GuardedDirectedCollectionValue<T>(innerlist[start, end]); } }
-
-
- /// <summary>
- /// Find the (first) index of an item in the wrapped collection
- /// </summary>
- /// <param name="item"></param>
- /// <returns></returns>
- public int IndexOf(T item) { return innerlist.IndexOf(item); }
-
-
- /// <summary>
- /// Find the last index of an item in the wrapped collection
- /// </summary>
- /// <param name="item"></param>
- /// <returns></returns>
- public int LastIndexOf(T item) { return innerlist.LastIndexOf(item); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="i"></param>
- /// <returns></returns>
- public T RemoveAt(int i)
- { throw new ReadOnlyCollectionException("List is read only"); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="start"></param>
- /// <param name="count"></param>
- public void RemoveInterval(int start, int count)
- { throw new ReadOnlyCollectionException("List is read only"); }
-
- #endregion
-
- #region IDirectedEnumerable<T> Members
-
- IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards()
- { return Backwards(); }
-
- #endregion
-
- #region IStack<T> Members
-
-
- /// <summary>
- ///
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <returns>-</returns>
- public void Push(T item)
- { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); }
-
- /// <summary>
- ///
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <returns>-</returns>
- public T Pop()
- { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); }
-
- #endregion
-
- #region IQueue<T> Members
-
- /// <summary>
- ///
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <returns>-</returns>
- public void Enqueue(T item)
- { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); }
-
- /// <summary>
- ///
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <returns>-</returns>
- public T Dequeue()
- { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); }
-
- #endregion
-
- #region IDisposable Members
-
- /// <summary>
- /// Ignore: this may be called by a foreach or using statement.
- /// </summary>
- public void Dispose() { }
-
- #endregion
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public override object Clone()
- {
- return new GuardedList<T>((IList<T>)(innerlist.Clone()));
- }
-
- #region System.Collections.Generic.IList<T> Members
-
- void System.Collections.Generic.IList<T>.RemoveAt(int index)
- {
- throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object");
- }
-
- void System.Collections.Generic.ICollection<T>.Add(T item)
- {
- throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object");
- }
-
- #endregion
-
- #region System.Collections.ICollection Members
-
- bool System.Collections.ICollection.IsSynchronized
- {
- get { return false; }
- }
-
- [Obsolete]
- Object System.Collections.ICollection.SyncRoot
- {
- get { return innerlist.SyncRoot; }
- }
-
- void System.Collections.ICollection.CopyTo(Array arr, int index)
- {
- if (index < 0 || index + Count > arr.Length)
- throw new ArgumentOutOfRangeException();
-
- foreach (T item in this)
- arr.SetValue(item, index++);
- }
-
- #endregion
-
- #region System.Collections.IList Members
-
- Object System.Collections.IList.this[int index]
- {
- get { return this[index]; }
- set
- {
- throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object");
- }
- }
-
- int System.Collections.IList.Add(Object o)
- {
- throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object");
- }
-
- bool System.Collections.IList.Contains(Object o)
- {
- return Contains((T)o);
- }
-
- int System.Collections.IList.IndexOf(Object o)
- {
- return Math.Max(-1, IndexOf((T)o));
- }
-
- void System.Collections.IList.Insert(int index, Object o)
- {
- throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object");
- }
-
- void System.Collections.IList.Remove(Object o)
- {
- throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object");
- }
-
- void System.Collections.IList.RemoveAt(int index)
- {
- throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object");
- }
-
- #endregion
- }
-
- /// <summary>
- /// A read-only wrapper for a generic indexable queue (allows indexing).
- ///
- /// <para>Suitable for wrapping a <see cref="T:C5.CircularQueue`1"/></para>
- /// </summary>
- /// <typeparam name="T">The item type.</typeparam>
- public class GuardedQueue<T> : GuardedDirectedCollectionValue<T>, IQueue<T>
- {
- #region Fields
-
- IQueue<T> queue;
-
- #endregion
-
- #region Constructor
-
- /// <summary>
- /// Wrap a queue in a read-only wrapper
- /// </summary>
- /// <param name="queue">The queue</param>
- public GuardedQueue(IQueue<T> queue) : base(queue) { this.queue = queue; }
-
- #endregion
-
- #region IQueue<T> Members
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public bool AllowsDuplicates { get { return queue.AllowsDuplicates; } }
-
- /// <summary>
- /// Index into the wrapped queue
- /// </summary>
- /// <param name="i"></param>
- /// <returns></returns>
- public T this[int i] { get { return queue[i]; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <returns>-</returns>
- public void Enqueue(T item)
- { throw new ReadOnlyCollectionException("Queue cannot be modified through this guard object"); }
-
- /// <summary>
- ///
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <returns>-</returns>
- public T Dequeue()
- { throw new ReadOnlyCollectionException("Queue cannot be modified through this guard object"); }
-
- #endregion
- }
-
- /// <summary>
- /// A read-only wrapper for a dictionary.
- ///
- /// <i>Suitable for wrapping a HashDictionary. <see cref="T:C5.HashDictionary`2"/></i>
- /// </summary>
- public class GuardedDictionary<K, V> : GuardedCollectionValue<KeyValuePair<K, V>>, IDictionary<K, V>
- {
- #region Fields
-
- IDictionary<K, V> dict;
-
- #endregion
-
- #region Constructor
-
- /// <summary>
- /// Wrap a dictionary in a read-only wrapper
- /// </summary>
- /// <param name="dict">the dictionary</param>
- public GuardedDictionary(IDictionary<K, V> dict) : base(dict) { this.dict = dict; }
-
- #endregion
-
- #region IDictionary<K,V> Members
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public SCG.IEqualityComparer<K> EqualityComparer { get { return dict.EqualityComparer; } }
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a
- /// read-only wrappper if used as a setter</exception>
- /// <value>Get the value corresponding to a key in the wrapped dictionary</value>
- public V this[K key]
- {
- get { return dict[key]; }
- set { throw new ReadOnlyCollectionException(); }
- }
-
- /// <summary>
- /// (This is a read-only wrapper)
- /// </summary>
- /// <value>True</value>
- public bool IsReadOnly { get { return true; } }
-
-
- //TODO: guard with a read-only wrapper? Probably so!
- /// <summary> </summary>
- /// <value>The collection of keys of the wrapped dictionary</value>
- public ICollectionValue<K> Keys
- { get { return dict.Keys; } }
-
-
- /// <summary> </summary>
- /// <value>The collection of values of the wrapped dictionary</value>
- public ICollectionValue<V> Values { get { return dict.Values; } }
-
- /// <summary>
- ///
- /// </summary>
- public virtual Fun<K, V> Fun { get { return delegate(K k) { return this[k]; }; } }
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="key"></param>
- /// <param name="val"></param>
- public void Add(K key, V val)
- { throw new ReadOnlyCollectionException(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="items"></param>
- public void AddAll<L, W>(SCG.IEnumerable<KeyValuePair<L, W>> items)
- where L : K
- where W : V
- { throw new ReadOnlyCollectionException(); }
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="key"></param>
- /// <returns></returns>
- public bool Remove(K key)
- { throw new ReadOnlyCollectionException(); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="key"></param>
- /// <param name="val"></param>
- /// <returns></returns>
- public bool Remove(K key, out V val)
- { throw new ReadOnlyCollectionException(); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- public void Clear()
- { throw new ReadOnlyCollectionException(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public Speed ContainsSpeed { get { return dict.ContainsSpeed; } }
-
- /// <summary>
- /// Check if the wrapped dictionary contains a specific key
- /// </summary>
- /// <param name="key">The key</param>
- /// <returns>True if it does</returns>
- public bool Contains(K key) { return dict.Contains(key); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="keys"></param>
- /// <returns></returns>
- public bool ContainsAll<H>(SCG.IEnumerable<H> keys) where H : K { return dict.ContainsAll(keys); }
-
- /// <summary>
- /// Search for a key in the wrapped dictionary, reporting the value if found
- /// </summary>
- /// <param name="key">The key</param>
- /// <param name="val">On exit: the value if found</param>
- /// <returns>True if found</returns>
- public bool Find(K key, out V val) { return dict.Find(key, out val); }
-
- /// <summary>
- /// Search for a key in the wrapped dictionary, reporting the value if found
- /// </summary>
- /// <param name="key">The key</param>
- /// <param name="val">On exit: the value if found</param>
- /// <returns>True if found</returns>
- public bool Find(ref K key, out V val) { return dict.Find(ref key, out val); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="key"></param>
- /// <param name="val"></param>
- /// <returns></returns>
- public bool Update(K key, V val)
- { throw new ReadOnlyCollectionException(); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="key"></param>
- /// <param name="val"></param>
- /// <param name="oldval"></param>
- /// <returns></returns>
- public bool Update(K key, V val, out V oldval)
- { throw new ReadOnlyCollectionException(); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="key"></param>
- /// <param name="val"></param>
- /// <returns></returns>
- public bool FindOrAdd(K key, ref V val)
- { throw new ReadOnlyCollectionException(); }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="key"></param>
- /// <param name="val"></param>
- /// <returns></returns>
- public bool UpdateOrAdd(K key, V val)
- { throw new ReadOnlyCollectionException(); }
-
- /// <summary>
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="key"></param>
- /// <param name="val"></param>
- /// <param name="oldval"></param>
- /// <returns></returns>
- public bool UpdateOrAdd(K key, V val, out V oldval)
- { throw new ReadOnlyCollectionException(); }
-
-
- /// <summary>
- /// Check the internal consistency of the wrapped dictionary
- /// </summary>
- /// <returns>True if check passed</returns>
- public bool Check() { return dict.Check(); }
-
- #endregion
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public virtual object Clone()
- {
- return new GuardedDictionary<K, V>((IDictionary<K, V>)(dict.Clone()));
- }
- }
-
-
-
- /// <summary>
- /// A read-only wrapper for a sorted dictionary.
- ///
- /// <i>Suitable for wrapping a Dictionary. <see cref="T:C5.Dictionary`2"/></i>
- /// </summary>
- public class GuardedSortedDictionary<K, V> : GuardedDictionary<K, V>, ISortedDictionary<K, V>
- {
- #region Fields
-
- ISortedDictionary<K, V> sorteddict;
-
- #endregion
-
- #region Constructor
-
- /// <summary>
- /// Wrap a sorted dictionary in a read-only wrapper
- /// </summary>
- /// <param name="sorteddict">the dictionary</param>
- public GuardedSortedDictionary(ISortedDictionary<K, V> sorteddict)
- : base(sorteddict)
- { this.sorteddict = sorteddict; }
-
- #endregion
-
- #region ISortedDictionary<K,V> Members
-
- /// <summary>
- /// The key comparer used by this dictionary.
- /// </summary>
- /// <value></value>
- public SCG.IComparer<K> Comparer { get { return sorteddict.Comparer; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public new ISorted<K> Keys { get { return null; } }
-
- /// <summary>
- /// Find the entry in the dictionary whose key is the
- /// predecessor of the specified key.
- /// </summary>
- /// <param name="key">The key</param>
- /// <param name="res">The predecessor, if any</param>
- /// <returns>True if key has a predecessor</returns>
- public bool TryPredecessor(K key, out KeyValuePair<K, V> res)
- {
- return sorteddict.TryPredecessor(key, out res);
- }
-
- /// <summary>
- /// Find the entry in the dictionary whose key is the
- /// successor of the specified key.
- /// </summary>
- /// <param name="key">The key</param>
- /// <param name="res">The successor, if any</param>
- /// <returns>True if the key has a successor</returns>
- public bool TrySuccessor(K key, out KeyValuePair<K, V> res)
- {
- return sorteddict.TrySuccessor(key, out res);
- }
-
- /// <summary>
- /// Find the entry in the dictionary whose key is the
- /// weak predecessor of the specified key.
- /// </summary>
- /// <param name="key">The key</param>
- /// <param name="res">The predecessor, if any</param>
- /// <returns>True if key has a weak predecessor</returns>
- public bool TryWeakPredecessor(K key, out KeyValuePair<K, V> res)
- {
- return sorteddict.TryWeakPredecessor(key, out res);
- }
-
- /// <summary>
- /// Find the entry in the dictionary whose key is the
- /// weak successor of the specified key.
- /// </summary>
- /// <param name="key">The key</param>
- /// <param name="res">The weak successor, if any</param>
- /// <returns>True if the key has a weak successor</returns>
- public bool TryWeakSuccessor(K key, out KeyValuePair<K, V> res)
- {
- return sorteddict.TryWeakSuccessor(key, out res);
- }
-
- /// <summary>
- /// Get the entry in the wrapped dictionary whose key is the
- /// predecessor of a specified key.
- /// </summary>
- /// <exception cref="NoSuchItemException"> if no such entry exists </exception>
- /// <param name="key">The key</param>
- /// <returns>The entry</returns>
- public KeyValuePair<K, V> Predecessor(K key)
- { return sorteddict.Predecessor(key); }
-
- /// <summary>
- /// Get the entry in the wrapped dictionary whose key is the
- /// successor of a specified key.
- /// </summary>
- /// <exception cref="NoSuchItemException"> if no such entry exists </exception>
- /// <param name="key">The key</param>
- /// <returns>The entry</returns>
- public KeyValuePair<K, V> Successor(K key)
- { return sorteddict.Successor(key); }
-
-
- /// <summary>
- /// Get the entry in the wrapped dictionary whose key is the
- /// weak predecessor of a specified key.
- /// </summary>
- /// <exception cref="NoSuchItemException"> if no such entry exists </exception>
- /// <param name="key">The key</param>
- /// <returns>The entry</returns>
- public KeyValuePair<K, V> WeakPredecessor(K key)
- { return sorteddict.WeakPredecessor(key); }
-
-
- /// <summary>
- /// Get the entry in the wrapped dictionary whose key is the
- /// weak successor of a specified key.
- /// </summary>
- /// <exception cref="NoSuchItemException"> if no such entry exists </exception>
- /// <param name="key">The key</param>
- /// <returns>The entry</returns>
- public KeyValuePair<K, V> WeakSuccessor(K key)
- { return sorteddict.WeakSuccessor(key); }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public KeyValuePair<K, V> FindMin()
- {
- return sorteddict.FindMin();
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <returns></returns>
- public KeyValuePair<K, V> DeleteMin()
- { throw new ReadOnlyCollectionException(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public KeyValuePair<K, V> FindMax()
- {
- return sorteddict.FindMax();
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <returns></returns>
- public KeyValuePair<K, V> DeleteMax()
- { throw new ReadOnlyCollectionException(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="c"></param>
- /// <param name="lowEntry"></param>
- /// <param name="lowIsValid"></param>
- /// <param name="highEntry"></param>
- /// <param name="highIsValid"></param>
- /// <returns></returns>
- public bool Cut(IComparable<K> c, out KeyValuePair<K, V> lowEntry, out bool lowIsValid, out KeyValuePair<K, V> highEntry, out bool highIsValid)
- {
- return sorteddict.Cut(c, out lowEntry, out lowIsValid, out highEntry, out highIsValid); ;
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="bot"></param>
- /// <returns></returns>
- public IDirectedEnumerable<KeyValuePair<K, V>> RangeFrom(K bot)
- {
- return new GuardedDirectedEnumerable<KeyValuePair<K, V>>(sorteddict.RangeFrom(bot));
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="bot"></param>
- /// <param name="top"></param>
- /// <returns></returns>
- public IDirectedEnumerable<KeyValuePair<K, V>> RangeFromTo(K bot, K top)
- {
- return new GuardedDirectedEnumerable<KeyValuePair<K, V>>(sorteddict.RangeFromTo(bot, top));
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="top"></param>
- /// <returns></returns>
- public IDirectedEnumerable<KeyValuePair<K, V>> RangeTo(K top)
- {
- return new GuardedDirectedEnumerable<KeyValuePair<K, V>>(sorteddict.RangeTo(top));
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public IDirectedCollectionValue<KeyValuePair<K, V>> RangeAll()
- {
- return new GuardedDirectedCollectionValue<KeyValuePair<K, V>>(sorteddict.RangeAll());
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="items"></param>
- public void AddSorted(System.Collections.Generic.IEnumerable<KeyValuePair<K, V>> items)
- { throw new ReadOnlyCollectionException(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="low"></param>
- public void RemoveRangeFrom(K low)
- { throw new ReadOnlyCollectionException(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="low"></param>
- /// <param name="hi"></param>
- public void RemoveRangeFromTo(K low, K hi)
- { throw new ReadOnlyCollectionException(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception>
- /// <param name="hi"></param>
- public void RemoveRangeTo(K hi)
- { throw new ReadOnlyCollectionException(); }
-
- #endregion
- }
-
+/* + Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +using System; +using System.Diagnostics; +using SCG = System.Collections.Generic; +namespace C5 +{ + /// <summary> + /// A read-only wrapper class for a generic enumerator + /// </summary> + public class GuardedEnumerator<T> : SCG.IEnumerator<T> + { + #region Fields + + SCG.IEnumerator<T> enumerator; + + #endregion + + #region Constructor + + /// <summary> + /// Create a wrapper around a generic enumerator + /// </summary> + /// <param name="enumerator">The enumerator to wrap</param> + public GuardedEnumerator(SCG.IEnumerator<T> enumerator) + { this.enumerator = enumerator; } + + #endregion + + #region IEnumerator<T> Members + + /// <summary> + /// Move wrapped enumerator to next item, or the first item if + /// this is the first call to MoveNext. + /// </summary> + /// <returns>True if enumerator is valid now</returns> + public bool MoveNext() { return enumerator.MoveNext(); } + + + /// <summary> + /// Undefined if enumerator is not valid (MoveNext hash been called returning true) + /// </summary> + /// <value>The current item of the wrapped enumerator.</value> + public T Current { get { return enumerator.Current; } } + + #endregion + + #region IDisposable Members + + //TODO: consider possible danger of calling through to Dispose. + /// <summary> + /// Dispose wrapped enumerator. + /// </summary> + public void Dispose() { enumerator.Dispose(); } + + #endregion + + + #region IEnumerator Members + + object System.Collections.IEnumerator.Current + { + get { return enumerator.Current; } + } + + void System.Collections.IEnumerator.Reset() + { + enumerator.Reset(); + } + + #endregion + } + + + + /// <summary> + /// A read-only wrapper class for a generic enumerable + /// + /// <i>This is mainly interesting as a base of other guard classes</i> + /// </summary> + public class GuardedEnumerable<T> : SCG.IEnumerable<T> + { + #region Fields + + SCG.IEnumerable<T> enumerable; + + #endregion + + #region Constructor + + /// <summary> + /// Wrap an enumerable in a read-only wrapper + /// </summary> + /// <param name="enumerable">The enumerable to wrap</param> + public GuardedEnumerable(SCG.IEnumerable<T> enumerable) + { this.enumerable = enumerable; } + + #endregion + + #region SCG.IEnumerable<T> Members + + /// <summary> + /// Get an enumerator from the wrapped enumerable + /// </summary> + /// <returns>The enumerator (itself wrapped)</returns> + public SCG.IEnumerator<T> GetEnumerator() + { return new GuardedEnumerator<T>(enumerable.GetEnumerator()); } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + + } + + + + /// <summary> + /// A read-only wrapper for a generic directed enumerable + /// + /// <i>This is mainly interesting as a base of other guard classes</i> + /// </summary> + public class GuardedDirectedEnumerable<T> : GuardedEnumerable<T>, IDirectedEnumerable<T> + { + #region Fields + + IDirectedEnumerable<T> directedenumerable; + + #endregion + + #region Constructor + + /// <summary> + /// Wrap a directed enumerable in a read-only wrapper + /// </summary> + /// <param name="directedenumerable">the collection to wrap</param> + public GuardedDirectedEnumerable(IDirectedEnumerable<T> directedenumerable) + : base(directedenumerable) + { this.directedenumerable = directedenumerable; } + + #endregion + + #region IDirectedEnumerable<T> Members + + /// <summary> + /// Get a enumerable that enumerates the wrapped collection in the opposite direction + /// </summary> + /// <returns>The mirrored enumerable</returns> + public IDirectedEnumerable<T> Backwards() + { return new GuardedDirectedEnumerable<T>(directedenumerable.Backwards()); } + + + /// <summary> + /// <code>Forwards</code> if same, else <code>Backwards</code> + /// </summary> + /// <value>The enumeration direction relative to the original collection.</value> + public EnumerationDirection Direction + { get { return directedenumerable.Direction; } } + + #endregion + } + + + + /// <summary> + /// A read-only wrapper for an ICollectionValue<T> + /// + /// <i>This is mainly interesting as a base of other guard classes</i> + /// </summary> + public class GuardedCollectionValue<T> : GuardedEnumerable<T>, ICollectionValue<T> + { + #region Events + /// <summary> + /// The ListenableEvents value of the wrapped collection + /// </summary> + /// <value></value> + public virtual EventTypeEnum ListenableEvents { get { return collectionvalue.ListenableEvents; } } + + /// <summary> + /// The ActiveEvents value of the wrapped collection + /// </summary> + /// <value></value> + public virtual EventTypeEnum ActiveEvents { get { return collectionvalue.ActiveEvents; } } + + ProxyEventBlock<T> eventBlock; + /// <summary> + /// The change event. Will be raised for every change operation on the collection. + /// </summary> + public event CollectionChangedHandler<T> CollectionChanged + { + add { (eventBlock ?? (eventBlock = new ProxyEventBlock<T>(this, collectionvalue))).CollectionChanged += value; } + remove { if (eventBlock != null) eventBlock.CollectionChanged -= value; } + } + + /// <summary> + /// The change event. Will be raised for every change operation on the collection. + /// </summary> + public event CollectionClearedHandler<T> CollectionCleared + { + add { (eventBlock ?? (eventBlock = new ProxyEventBlock<T>(this, collectionvalue))).CollectionCleared += value; } + remove { if (eventBlock != null) eventBlock.CollectionCleared -= value; } + } + + /// <summary> + /// The item added event. Will be raised for every individual addition to the collection. + /// </summary> + public event ItemsAddedHandler<T> ItemsAdded + { + add { (eventBlock ?? (eventBlock = new ProxyEventBlock<T>(this, collectionvalue))).ItemsAdded += value; } + remove { if (eventBlock != null) eventBlock.ItemsAdded -= value; } + } + + /// <summary> + /// The item added event. Will be raised for every individual addition to the collection. + /// </summary> + public event ItemInsertedHandler<T> ItemInserted + { + add { (eventBlock ?? (eventBlock = new ProxyEventBlock<T>(this, collectionvalue))).ItemInserted += value; } + remove { if (eventBlock != null) eventBlock.ItemInserted -= value; } + } + + /// <summary> + /// The item removed event. Will be raised for every individual removal from the collection. + /// </summary> + public event ItemsRemovedHandler<T> ItemsRemoved + { + add { (eventBlock ?? (eventBlock = new ProxyEventBlock<T>(this, collectionvalue))).ItemsRemoved += value; } + remove { if (eventBlock != null) eventBlock.ItemsRemoved -= value; } + } + + /// <summary> + /// The item removed event. Will be raised for every individual removal from the collection. + /// </summary> + public event ItemRemovedAtHandler<T> ItemRemovedAt + { + add { (eventBlock ?? (eventBlock = new ProxyEventBlock<T>(this, collectionvalue))).ItemRemovedAt += value; } + remove { if (eventBlock != null) eventBlock.ItemRemovedAt -= value; } + } + #endregion + + #region Fields + + ICollectionValue<T> collectionvalue; + + #endregion + + #region Constructor + + /// <summary> + /// Wrap a ICollectionValue<T> in a read-only wrapper + /// </summary> + /// <param name="collectionvalue">the collection to wrap</param> + public GuardedCollectionValue(ICollectionValue<T> collectionvalue) + : base(collectionvalue) + { this.collectionvalue = collectionvalue; } + + #endregion + + #region ICollection<T> Members + + /// <summary> + /// Get the size of the wrapped collection + /// </summary> + /// <value>The size</value> + public virtual bool IsEmpty { get { return collectionvalue.IsEmpty; } } + + /// <summary> + /// Get the size of the wrapped collection + /// </summary> + /// <value>The size</value> + public virtual int Count { get { return collectionvalue.Count; } } + + /// <summary> + /// The value is symbolic indicating the type of asymptotic complexity + /// in terms of the size of this collection (worst-case or amortized as + /// relevant). + /// </summary> + /// <value>A characterization of the speed of the + /// <code>Count</code> property in this collection.</value> + public virtual Speed CountSpeed { get { return collectionvalue.CountSpeed; } } + + /// <summary> + /// Copy the items of the wrapped collection to an array + /// </summary> + /// <param name="a">The array</param> + /// <param name="i">Starting offset</param> + public virtual void CopyTo(T[] a, int i) { collectionvalue.CopyTo(a, i); } + + /// <summary> + /// Create an array from the items of the wrapped collection + /// </summary> + /// <returns>The array</returns> + public virtual T[] ToArray() { return collectionvalue.ToArray(); } + + /// <summary> + /// Apply a delegate to all items of the wrapped enumerable. + /// </summary> + /// <param name="a">The delegate to apply</param> + //TODO: change this to throw an exception? + public virtual void Apply(Act<T> a) { collectionvalue.Apply(a); } + + + /// <summary> + /// Check if there exists an item that satisfies a + /// specific predicate in the wrapped enumerable. + /// </summary> + /// <param name="filter">A filter delegate + /// (<see cref="T:C5.Filter`1"/>) defining the predicate</param> + /// <returns>True is such an item exists</returns> + public virtual bool Exists(Fun<T, bool> filter) { return collectionvalue.Exists(filter); } + + /// <summary> + /// + /// </summary> + /// <param name="filter"></param> + /// <param name="item"></param> + /// <returns></returns> + public virtual bool Find(Fun<T, bool> filter, out T item) { return collectionvalue.Find(filter, out item); } + + /// <summary> + /// Check if all items in the wrapped enumerable satisfies a specific predicate. + /// </summary> + /// <param name="filter">A filter delegate + /// (<see cref="T:C5.Filter`1"/>) defining the predicate</param> + /// <returns>True if all items satisfies the predicate</returns> + public virtual bool All(Fun<T, bool> filter) { return collectionvalue.All(filter); } + + /// <summary> + /// Create an enumerable, enumerating the items of this collection that satisfies + /// a certain condition. + /// </summary> + /// <param name="filter">The T->bool filter delegate defining the condition</param> + /// <returns>The filtered enumerable</returns> + public virtual SCG.IEnumerable<T> Filter(Fun<T, bool> filter) { return collectionvalue.Filter(filter); } + + /// <summary> + /// Choose some item of this collection. + /// </summary> + /// <exception cref="NoSuchItemException">if collection is empty.</exception> + /// <returns></returns> + public virtual T Choose() { return collectionvalue.Choose(); } + + #endregion + + #region IShowable Members + + /// <summary> + /// + /// </summary> + /// <param name="stringbuilder"></param> + /// <param name="formatProvider"></param> + /// <param name="rest"></param> + /// <returns></returns> + public bool Show(System.Text.StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider) + { + return collectionvalue.Show(stringbuilder, ref rest, formatProvider); + } + #endregion + + #region IFormattable Members + + /// <summary> + /// + /// </summary> + /// <param name="format"></param> + /// <param name="formatProvider"></param> + /// <returns></returns> + public string ToString(string format, IFormatProvider formatProvider) + { + return collectionvalue.ToString(format, formatProvider); + } + + #endregion + } + + + + /// <summary> + /// A read-only wrapper for a directed collection + /// + /// <i>This is mainly interesting as a base of other guard classes</i> + /// </summary> + public class GuardedDirectedCollectionValue<T> : GuardedCollectionValue<T>, IDirectedCollectionValue<T> + { + #region Fields + + IDirectedCollectionValue<T> directedcollection; + + #endregion + + #region Constructor + + /// <summary> + /// Wrap a directed collection in a read-only wrapper + /// </summary> + /// <param name="directedcollection">the collection to wrap</param> + public GuardedDirectedCollectionValue(IDirectedCollectionValue<T> directedcollection) + : + base(directedcollection) + { this.directedcollection = directedcollection; } + + #endregion + + #region IDirectedCollection<T> Members + + /// <summary> + /// Get a collection that enumerates the wrapped collection in the opposite direction + /// </summary> + /// <returns>The mirrored collection</returns> + public virtual IDirectedCollectionValue<T> Backwards() + { return new GuardedDirectedCollectionValue<T>(directedcollection.Backwards()); } + + /// <summary> + /// + /// </summary> + /// <param name="predicate"></param> + /// <param name="item"></param> + /// <returns></returns> + public virtual bool FindLast(Fun<T, bool> predicate, out T item) { return directedcollection.FindLast(predicate, out item); } + + #endregion + + #region IDirectedEnumerable<T> Members + + IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() + { return Backwards(); } + + + /// <summary> + /// <code>Forwards</code> if same, else <code>Backwards</code> + /// </summary> + /// <value>The enumeration direction relative to the original collection.</value> + public EnumerationDirection Direction + { get { return directedcollection.Direction; } } + + #endregion + } + + + + /// <summary> + /// A read-only wrapper for an <see cref="T:C5.ICollection`1"/>, + /// <para> + /// <i>Suitable for wrapping hash tables, <see cref="T:C5.HashSet`1"/> + /// and <see cref="T:C5.HashBag`1"/> </i></para> + /// </summary> + public class GuardedCollection<T> : GuardedCollectionValue<T>, ICollection<T> + { + #region Fields + + ICollection<T> collection; + + #endregion + + #region Constructor + + /// <summary> + /// Wrap an ICollection<T> in a read-only wrapper + /// </summary> + /// <param name="collection">the collection to wrap</param> + public GuardedCollection(ICollection<T> collection) + : base(collection) + { + this.collection = collection; + } + + #endregion + + #region ICollection<T> Members + + /// <summary> + /// (This is a read-only wrapper) + /// </summary> + /// <value>True</value> + public virtual bool IsReadOnly { get { return true; } } + + + /// <summary> </summary> + /// <value>Speed of wrapped collection</value> + public virtual Speed ContainsSpeed { get { return collection.ContainsSpeed; } } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public virtual int GetUnsequencedHashCode() + { return collection.GetUnsequencedHashCode(); } + + /// <summary> + /// + /// </summary> + /// <param name="that"></param> + /// <returns></returns> + public virtual bool UnsequencedEquals(ICollection<T> that) + { return collection.UnsequencedEquals(that); } + + + /// <summary> + /// Check if an item is in the wrapped collection + /// </summary> + /// <param name="item">The item</param> + /// <returns>True if found</returns> + public virtual bool Contains(T item) { return collection.Contains(item); } + + + /// <summary> + /// Count the number of times an item appears in the wrapped collection + /// </summary> + /// <param name="item">The item</param> + /// <returns>The number of copies</returns> + public virtual int ContainsCount(T item) { return collection.ContainsCount(item); } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public virtual ICollectionValue<T> UniqueItems() { return new GuardedCollectionValue<T>(collection.UniqueItems()); } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public virtual ICollectionValue<KeyValuePair<T, int>> ItemMultiplicities() { return new GuardedCollectionValue<KeyValuePair<T, int>>(collection.ItemMultiplicities()); } + + /// <summary> + /// Check if all items in the argument is in the wrapped collection + /// </summary> + /// <param name="items">The items</param> + /// <typeparam name="U"></typeparam> + /// <returns>True if so</returns> + public virtual bool ContainsAll<U>(SCG.IEnumerable<U> items) where U : T { return collection.ContainsAll(items); } + + /// <summary> + /// Search for an item in the wrapped collection + /// </summary> + /// <param name="item">On entry the item to look for, on exit the equivalent item found (if any)</param> + /// <returns></returns> + public virtual bool Find(ref T item) { return collection.Find(ref item); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="item"></param> + /// <returns></returns> + public virtual bool FindOrAdd(ref T item) + { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="item"></param> + /// <returns></returns> + public virtual bool Update(T item) + { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="item"></param> + /// <param name="olditem"></param> + /// <returns></returns> + public virtual bool Update(T item, out T olditem) + { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="item"></param> + /// <returns></returns> + public virtual bool UpdateOrAdd(T item) + { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="item"></param> + /// <param name="olditem"></param> + /// <returns></returns> + public virtual bool UpdateOrAdd(T item, out T olditem) + { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); } + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="item"></param> + /// <returns></returns> + public virtual bool Remove(T item) + { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="item">The value to remove.</param> + /// <param name="removeditem">The removed value.</param> + /// <returns></returns> + public virtual bool Remove(T item, out T removeditem) + { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="item"></param> + public virtual void RemoveAllCopies(T item) + { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <typeparam name="U"></typeparam> + /// <param name="items"></param> + public virtual void RemoveAll<U>(SCG.IEnumerable<U> items) where U : T + { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); } + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + public virtual void Clear() + { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <typeparam name="U"></typeparam> + /// <param name="items"></param> + public virtual void RetainAll<U>(SCG.IEnumerable<U> items) where U : T + { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); } + + /// <summary> + /// Check wrapped collection for internal consistency + /// </summary> + /// <returns>True if check passed</returns> + public virtual bool Check() { return collection.Check(); } + + #endregion + + #region IExtensible<T> Members + + /// <summary> </summary> + /// <value>False if wrapped collection has set semantics</value> + public virtual bool AllowsDuplicates { get { return collection.AllowsDuplicates; } } + + //TODO: the equalityComparer should be guarded + /// <summary> + /// + /// </summary> + /// <value></value> + public virtual SCG.IEqualityComparer<T> EqualityComparer { get { return collection.EqualityComparer; } } + + /// <summary> + /// By convention this is true for any collection with set semantics. + /// </summary> + /// <value>True if only one representative of a group of equal items + /// is kept in the collection together with the total count.</value> + public virtual bool DuplicatesByCounting { get { return collection.DuplicatesByCounting; } } + + + /// <summary> </summary> + /// <value>True if wrapped collection is empty</value> + public override bool IsEmpty { get { return collection.IsEmpty; } } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="item"></param> + /// <returns></returns> + public virtual bool Add(T item) + { throw new ReadOnlyCollectionException(); } + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="item"></param> + void SCG.ICollection<T>.Add(T item) + { throw new ReadOnlyCollectionException(); } + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <typeparam name="U"></typeparam> + /// <param name="items"></param> + public virtual void AddAll<U>(SCG.IEnumerable<U> items) where U : T + { throw new ReadOnlyCollectionException(); } + + #endregion + + #region ICloneable Members + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public virtual object Clone() + { + return new GuardedCollection<T>((ICollection<T>)(collection.Clone())); + } + + #endregion + + } + + + /// <summary> + /// A read-only wrapper for a sequenced collection + /// + /// <i>This is mainly interesting as a base of other guard classes</i> + /// </summary> + public class GuardedSequenced<T> : GuardedCollection<T>, ISequenced<T> + { + #region Fields + + ISequenced<T> sequenced; + + #endregion + + #region Constructor + + /// <summary> + /// Wrap a sequenced collection in a read-only wrapper + /// </summary> + /// <param name="sorted"></param> + public GuardedSequenced(ISequenced<T> sorted) : base(sorted) { this.sequenced = sorted; } + + #endregion + + /// <summary> + /// Check if there exists an item that satisfies a + /// specific predicate in this collection and return the index of the first one. + /// </summary> + /// <param name="predicate">A delegate + /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param> + /// <returns>the index, if found, a negative value else</returns> + public int FindIndex(Fun<T, bool> predicate) + { + IIndexed<T> indexed = sequenced as IIndexed<T>; + if (indexed != null) + return indexed.FindIndex(predicate); + int index = 0; + foreach (T item in this) + { + if (predicate(item)) + return index; + index++; + } + return -1; + } + + /// <summary> + /// Check if there exists an item that satisfies a + /// specific predicate in this collection and return the index of the last one. + /// </summary> + /// <param name="predicate">A delegate + /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param> + /// <returns>the index, if found, a negative value else</returns> + public int FindLastIndex(Fun<T, bool> predicate) + { + IIndexed<T> indexed = sequenced as IIndexed<T>; + if (indexed != null) + return indexed.FindLastIndex(predicate); + int index = Count - 1; + foreach (T item in Backwards()) + { + if (predicate(item)) + return index; + index--; + } + return -1; + } + + + + #region ISequenced<T> Members + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public int GetSequencedHashCode() + { return sequenced.GetSequencedHashCode(); } + + /// <summary> + /// + /// </summary> + /// <param name="that"></param> + /// <returns></returns> + public bool SequencedEquals(ISequenced<T> that) + { return sequenced.SequencedEquals(that); } + + #endregion + + #region IDirectedCollection<T> Members + + /// <summary> + /// Get a collection that enumerates the wrapped collection in the opposite direction + /// </summary> + /// <returns>The mirrored collection</returns> + public virtual IDirectedCollectionValue<T> Backwards() + { return new GuardedDirectedCollectionValue<T>(sequenced.Backwards()); } + + /// <summary> + /// + /// </summary> + /// <param name="predicate"></param> + /// <param name="item"></param> + /// <returns></returns> + public virtual bool FindLast(Fun<T, bool> predicate, out T item) { return sequenced.FindLast(predicate, out item); } + + #endregion + + #region IDirectedEnumerable<T> Members + + IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() + { return Backwards(); } + + + + /// <summary> + /// <code>Forwards</code> if same, else <code>Backwards</code> + /// </summary> + /// <value>The enumeration direction relative to the original collection.</value> + public EnumerationDirection Direction + { get { return EnumerationDirection.Forwards; } } + + #endregion + + #region ICloneable Members + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public override object Clone() + { + return new GuardedCollection<T>((ISequenced<T>)(sequenced.Clone())); + } + + #endregion + + } + + + /// <summary> + /// A read-only wrapper for a sorted collection + /// + /// <i>This is mainly interesting as a base of other guard classes</i> + /// </summary> + public class GuardedSorted<T> : GuardedSequenced<T>, ISorted<T> + { + #region Fields + + ISorted<T> sorted; + + #endregion + + #region Constructor + + /// <summary> + /// Wrap a sorted collection in a read-only wrapper + /// </summary> + /// <param name="sorted"></param> + public GuardedSorted(ISorted<T> sorted) : base(sorted) { this.sorted = sorted; } + + #endregion + + #region ISorted<T> Members + + /// <summary> + /// Find the strict predecessor of item in the guarded sorted collection, + /// that is, the greatest item in the collection smaller than the item. + /// </summary> + /// <param name="item">The item to find the predecessor for.</param> + /// <param name="res">The predecessor, if any; otherwise the default value for T.</param> + /// <returns>True if item has a predecessor; otherwise false.</returns> + public bool TryPredecessor(T item, out T res) { return sorted.TryPredecessor(item, out res); } + + + /// <summary> + /// Find the strict successor of item in the guarded sorted collection, + /// that is, the least item in the collection greater than the supplied value. + /// </summary> + /// <param name="item">The item to find the successor for.</param> + /// <param name="res">The successor, if any; otherwise the default value for T.</param> + /// <returns>True if item has a successor; otherwise false.</returns> + public bool TrySuccessor(T item, out T res) { return sorted.TrySuccessor(item, out res); } + + + /// <summary> + /// Find the weak predecessor of item in the guarded sorted collection, + /// that is, the greatest item in the collection smaller than or equal to the item. + /// </summary> + /// <param name="item">The item to find the weak predecessor for.</param> + /// <param name="res">The weak predecessor, if any; otherwise the default value for T.</param> + /// <returns>True if item has a weak predecessor; otherwise false.</returns> + public bool TryWeakPredecessor(T item, out T res) { return sorted.TryWeakPredecessor(item, out res); } + + + /// <summary> + /// Find the weak successor of item in the sorted collection, + /// that is, the least item in the collection greater than or equal to the supplied value. + /// </summary> + /// <param name="item">The item to find the weak successor for.</param> + /// <param name="res">The weak successor, if any; otherwise the default value for T.</param> + /// <returns>True if item has a weak successor; otherwise false.</returns> + public bool TryWeakSuccessor(T item, out T res) { return sorted.TryWeakSuccessor(item, out res); } + + + /// <summary> + /// Find the predecessor of the item in the wrapped sorted collection + /// </summary> + /// <exception cref="NoSuchItemException"> if no such element exists </exception> + /// <param name="item">The item</param> + /// <returns>The predecessor</returns> + public T Predecessor(T item) { return sorted.Predecessor(item); } + + + /// <summary> + /// Find the Successor of the item in the wrapped sorted collection + /// </summary> + /// <exception cref="NoSuchItemException"> if no such element exists </exception> + /// <param name="item">The item</param> + /// <returns>The Successor</returns> + public T Successor(T item) { return sorted.Successor(item); } + + + /// <summary> + /// Find the weak predecessor of the item in the wrapped sorted collection + /// </summary> + /// <exception cref="NoSuchItemException"> if no such element exists </exception> + /// <param name="item">The item</param> + /// <returns>The weak predecessor</returns> + public T WeakPredecessor(T item) { return sorted.WeakPredecessor(item); } + + + /// <summary> + /// Find the weak Successor of the item in the wrapped sorted collection + /// </summary> + /// <exception cref="NoSuchItemException"> if no such element exists </exception> + /// <param name="item">The item</param> + /// <returns>The weak Successor</returns> + public T WeakSuccessor(T item) { return sorted.WeakSuccessor(item); } + + + /// <summary> + /// Run Cut on the wrapped sorted collection + /// </summary> + /// <param name="c"></param> + /// <param name="low"></param> + /// <param name="lval"></param> + /// <param name="high"></param> + /// <param name="hval"></param> + /// <returns></returns> + public bool Cut(IComparable<T> c, out T low, out bool lval, out T high, out bool hval) + { return sorted.Cut(c, out low, out lval, out high, out hval); } + + + /// <summary> + /// Get the specified range from the wrapped collection. + /// (The current implementation erroneously does not wrap the result.) + /// </summary> + /// <param name="bot"></param> + /// <returns></returns> + public IDirectedEnumerable<T> RangeFrom(T bot) { return sorted.RangeFrom(bot); } + + + /// <summary> + /// Get the specified range from the wrapped collection. + /// (The current implementation erroneously does not wrap the result.) + /// </summary> + /// <param name="bot"></param> + /// <param name="top"></param> + /// <returns></returns> + public IDirectedEnumerable<T> RangeFromTo(T bot, T top) + { return sorted.RangeFromTo(bot, top); } + + + /// <summary> + /// Get the specified range from the wrapped collection. + /// (The current implementation erroneously does not wrap the result.) + /// </summary> + /// <param name="top"></param> + /// <returns></returns> + public IDirectedEnumerable<T> RangeTo(T top) { return sorted.RangeTo(top); } + + + /// <summary> + /// Get the specified range from the wrapped collection. + /// (The current implementation erroneously does not wrap the result.) + /// </summary> + /// <returns></returns> + public IDirectedCollectionValue<T> RangeAll() { return sorted.RangeAll(); } + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="items"></param> + /// <typeparam name="U"></typeparam> + public void AddSorted<U>(SCG.IEnumerable<U> items) where U : T + { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); } + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="low"></param> + public void RemoveRangeFrom(T low) + { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="low"></param> + /// <param name="hi"></param> + public void RemoveRangeFromTo(T low, T hi) + { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="hi"></param> + public void RemoveRangeTo(T hi) + { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); } + + #endregion + + #region IPriorityQueue<T> Members + + /// <summary> + /// Find the minimum of the wrapped collection + /// </summary> + /// <returns>The minimum</returns> + public T FindMin() { return sorted.FindMin(); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <returns></returns> + public T DeleteMin() + { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); } + + + /// <summary> + /// Find the maximum of the wrapped collection + /// </summary> + /// <returns>The maximum</returns> + public T FindMax() { return sorted.FindMax(); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <returns></returns> + public T DeleteMax() + { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); } + + //TODO: we should guard the comparer! + /// <summary> + /// The comparer object supplied at creation time for the underlying collection + /// </summary> + /// <value>The comparer</value> + public SCG.IComparer<T> Comparer { get { return sorted.Comparer; } } + #endregion + + #region IDirectedEnumerable<T> Members + + IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() + { return Backwards(); } + + #endregion + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public override object Clone() + { + return new GuardedSorted<T>((ISorted<T>)(sorted.Clone())); + } + + } + + + + /// <summary> + /// Read-only wrapper for indexed sorted collections + /// + /// <i>Suitable for wrapping TreeSet, TreeBag and SortedArray</i> + /// </summary> + public class GuardedIndexedSorted<T> : GuardedSorted<T>, IIndexedSorted<T> + { + #region Fields + + IIndexedSorted<T> indexedsorted; + + #endregion + + #region Constructor + + /// <summary> + /// Wrap an indexed sorted collection in a read-only wrapper + /// </summary> + /// <param name="list">the indexed sorted collection</param> + public GuardedIndexedSorted(IIndexedSorted<T> list) + : base(list) + { this.indexedsorted = list; } + + #endregion + + #region IIndexedSorted<T> Members + + /// <summary> + /// Get the specified range from the wrapped collection. + /// (The current implementation erroneously does not wrap the result.) + /// </summary> + /// <param name="bot"></param> + /// <returns></returns> + public new IDirectedCollectionValue<T> RangeFrom(T bot) + { return indexedsorted.RangeFrom(bot); } + + + /// <summary> + /// Get the specified range from the wrapped collection. + /// (The current implementation erroneously does not wrap the result.) + /// </summary> + /// <param name="bot"></param> + /// <param name="top"></param> + /// <returns></returns> + public new IDirectedCollectionValue<T> RangeFromTo(T bot, T top) + { return indexedsorted.RangeFromTo(bot, top); } + + + /// <summary> + /// Get the specified range from the wrapped collection. + /// (The current implementation erroneously does not wrap the result.) + /// </summary> + /// <param name="top"></param> + /// <returns></returns> + public new IDirectedCollectionValue<T> RangeTo(T top) + { return indexedsorted.RangeTo(top); } + + + /// <summary> + /// Report the number of items in the specified range of the wrapped collection + /// </summary> + /// <param name="bot"></param> + /// <returns></returns> + public int CountFrom(T bot) { return indexedsorted.CountFrom(bot); } + + + /// <summary> + /// Report the number of items in the specified range of the wrapped collection + /// </summary> + /// <param name="bot"></param> + /// <param name="top"></param> + /// <returns></returns> + public int CountFromTo(T bot, T top) { return indexedsorted.CountFromTo(bot, top); } + + + /// <summary> + /// Report the number of items in the specified range of the wrapped collection + /// </summary> + /// <param name="top"></param> + /// <returns></returns> + public int CountTo(T top) { return indexedsorted.CountTo(top); } + + + /// <summary> + /// Run FindAll on the wrapped collection with the indicated filter. + /// The result will <b>not</b> be read-only. + /// </summary> + /// <param name="f"></param> + /// <returns></returns> + public IIndexedSorted<T> FindAll(Fun<T, bool> f) + { return indexedsorted.FindAll(f); } + + + /// <summary> + /// Run Map on the wrapped collection with the indicated mapper. + /// The result will <b>not</b> be read-only. + /// </summary> + /// <param name="m"></param> + /// <param name="c">The comparer to use in the result</param> + /// <returns></returns> + public IIndexedSorted<V> Map<V>(Fun<T, V> m, SCG.IComparer<V> c) + { return indexedsorted.Map(m, c); } + + #endregion + + #region IIndexed<T> Members + + /// <summary> + /// + /// </summary> + /// <value>The i'th item of the wrapped sorted collection</value> + public T this[int i] { get { return indexedsorted[i]; } } + + /// <summary> + /// + /// </summary> + /// <value></value> + public virtual Speed IndexingSpeed { get { return indexedsorted.IndexingSpeed; } } + + /// <summary> </summary> + /// <value>A directed collection of the items in the indicated interval of the wrapped collection</value> + public IDirectedCollectionValue<T> this[int start, int end] + { get { return new GuardedDirectedCollectionValue<T>(indexedsorted[start, end]); } } + + + /// <summary> + /// Find the (first) index of an item in the wrapped collection + /// </summary> + /// <param name="item"></param> + /// <returns></returns> + public int IndexOf(T item) { return indexedsorted.IndexOf(item); } + + + /// <summary> + /// Find the last index of an item in the wrapped collection + /// </summary> + /// <param name="item"></param> + /// <returns></returns> + public int LastIndexOf(T item) { return indexedsorted.LastIndexOf(item); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="i"></param> + /// <returns></returns> + public T RemoveAt(int i) + { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="start"></param> + /// <param name="count"></param> + public void RemoveInterval(int start, int count) + { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); } + + #endregion + + #region IDirectedEnumerable<T> Members + + IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() + { return Backwards(); } + + #endregion + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public override object Clone() + { + return new GuardedIndexedSorted<T>((IIndexedSorted<T>)(indexedsorted.Clone())); + } + + } + + + + /// <summary> + /// A read-only wrapper for a generic list collection + /// <i>Suitable as a wrapper for LinkedList, HashedLinkedList, ArrayList and HashedArray. + /// <see cref="T:C5.LinkedList`1"/>, + /// <see cref="T:C5.HashedLinkedList`1"/>, + /// <see cref="T:C5.ArrayList`1"/> or + /// <see cref="T:C5.HashedArray`1"/>. + /// </i> + /// </summary> + public class GuardedList<T> : GuardedSequenced<T>, IList<T>, SCG.IList<T> + { + #region Fields + + IList<T> innerlist; + GuardedList<T> underlying; + bool slidableView = false; + + #endregion + + #region Constructor + + /// <summary> + /// Wrap a list in a read-only wrapper. A list gets wrapped as read-only, + /// a list view gets wrapped as read-only and non-slidable. + /// </summary> + /// <param name="list">The list</param> + public GuardedList(IList<T> list) + : base(list) + { + this.innerlist = list; + // If wrapping a list view, make innerlist = the view, and make + // underlying = a guarded version of the view's underlying list + if (list.Underlying != null) + underlying = new GuardedList<T>(list.Underlying, null, false); + } + + GuardedList(IList<T> list, GuardedList<T> underlying, bool slidableView) + : base(list) + { + this.innerlist = list; this.underlying = underlying; this.slidableView = slidableView; + } + #endregion + + #region IList<T> Members + + /// <summary> + /// + /// </summary> + /// <value>The first item of the wrapped list</value> + public T First { get { return innerlist.First; } } + + + /// <summary> + /// + /// </summary> + /// <value>The last item of the wrapped list</value> + public T Last { get { return innerlist.Last; } } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> if used as setter</exception> + /// <value>True if wrapped list has FIFO semantics for the Add(T item) and Remove() methods</value> + public bool FIFO + { + get { return innerlist.FIFO; } + set { throw new ReadOnlyCollectionException("List is read only"); } + } + + /// <summary> + /// + /// </summary> + public virtual bool IsFixedSize + { + get { return true; } + } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> if used as setter</exception> + /// <value>The i'th item of the wrapped list</value> + public T this[int i] + { + get { return innerlist[i]; } + set { throw new ReadOnlyCollectionException("List is read only"); } + } + + /// <summary> + /// + /// </summary> + /// <value></value> + public virtual Speed IndexingSpeed { get { return innerlist.IndexingSpeed; } } + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="index"></param> + /// <param name="item"></param> + public void Insert(int index, T item) + { throw new ReadOnlyCollectionException(); } + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="pointer"></param> + /// <param name="item"></param> + public void Insert(IList<T> pointer, T item) + { throw new ReadOnlyCollectionException(); } + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="item"></param> + public void InsertFirst(T item) + { throw new ReadOnlyCollectionException("List is read only"); } + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="item"></param> + public void InsertLast(T item) + { throw new ReadOnlyCollectionException("List is read only"); } + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="item"></param> + /// <param name="target"></param> + public void InsertBefore(T item, T target) + { throw new ReadOnlyCollectionException("List is read only"); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="item"></param> + /// <param name="target"></param> + public void InsertAfter(T item, T target) + { throw new ReadOnlyCollectionException("List is read only"); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="i"></param> + /// <param name="items"></param> + public void InsertAll<U>(int i, SCG.IEnumerable<U> items) where U : T + { throw new ReadOnlyCollectionException("List is read only"); } + + + /// <summary> + /// Perform FindAll on the wrapped list. The result is <b>not</b> necessarily read-only. + /// </summary> + /// <param name="filter">The filter to use</param> + /// <returns></returns> + public IList<T> FindAll(Fun<T, bool> filter) { return innerlist.FindAll(filter); } + + + /// <summary> + /// Perform Map on the wrapped list. The result is <b>not</b> necessarily read-only. + /// </summary> + /// <typeparam name="V">The type of items of the new list</typeparam> + /// <param name="mapper">The mapper to use.</param> + /// <returns>The mapped list</returns> + public IList<V> Map<V>(Fun<T, V> mapper) { return innerlist.Map(mapper); } + + /// <summary> + /// Perform Map on the wrapped list. The result is <b>not</b> necessarily read-only. + /// </summary> + /// <typeparam name="V">The type of items of the new list</typeparam> + /// <param name="mapper">The delegate defining the map.</param> + /// <param name="itemequalityComparer">The itemequalityComparer to use for the new list</param> + /// <returns>The new list.</returns> + public IList<V> Map<V>(Fun<T, V> mapper, SCG.IEqualityComparer<V> itemequalityComparer) { return innerlist.Map(mapper, itemequalityComparer); } + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <returns></returns> + public T Remove() { throw new ReadOnlyCollectionException("List is read only"); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <returns></returns> + public T RemoveFirst() { throw new ReadOnlyCollectionException("List is read only"); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <returns></returns> + public T RemoveLast() { throw new ReadOnlyCollectionException("List is read only"); } + + + /// <summary> + /// Create the indicated view on the wrapped list and wrap it read-only. + /// </summary> + /// <param name="start"></param> + /// <param name="count"></param> + /// <returns></returns> + public IList<T> View(int start, int count) + { + IList<T> view = innerlist.View(start, count); + return view == null ? null : new GuardedList<T>(view, underlying ?? this, true); + } + + /// <summary> + /// Create the indicated view on the wrapped list and wrap it read-only. + /// </summary> + /// <param name="item"></param> + /// <returns></returns> + public IList<T> ViewOf(T item) + { + IList<T> view = innerlist.ViewOf(item); + return view == null ? null : new GuardedList<T>(view, underlying ?? this, true); + } + + /// <summary> + /// Create the indicated view on the wrapped list and wrap it read-only. + /// </summary> + /// <param name="item"></param> + /// <returns></returns> + public IList<T> LastViewOf(T item) + { + IList<T> view = innerlist.LastViewOf(item); + return view == null ? null : new GuardedList<T>(view, underlying ?? this, true); + } + + + /// <summary> + /// </summary> + /// <value>The wrapped underlying list of the wrapped view </value> + public IList<T> Underlying { get { return underlying; } } + + + /// <summary> + /// + /// </summary> + /// <value>The offset of the wrapped list as a view.</value> + public int Offset { get { return innerlist.Offset; } } + + /// <summary> + /// + /// </summary> + /// <value></value> + public virtual bool IsValid { get { return innerlist.IsValid; } } + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> if this is a wrapped view and not a view that was made on a wrapper</exception> + /// <param name="offset"></param> + public IList<T> Slide(int offset) + { + if (slidableView) + { + innerlist.Slide(offset); + return this; + } + else + throw new ReadOnlyCollectionException("List is read only"); + } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="offset"></param> + /// <param name="size"></param> + public IList<T> Slide(int offset, int size) + { + if (slidableView) + { + innerlist.Slide(offset, size); + return this; + } + else + throw new ReadOnlyCollectionException("List is read only"); + } + + + /// <summary> + /// + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="offset"></param> + /// <returns></returns> + public bool TrySlide(int offset) + { + if (slidableView) + return innerlist.TrySlide(offset); + else + throw new ReadOnlyCollectionException("List is read only"); + } + + /// <summary> + /// + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="offset"></param> + /// <param name="size"></param> + /// <returns></returns> + public bool TrySlide(int offset, int size) + { + if (slidableView) + return innerlist.TrySlide(offset, size); + else + throw new ReadOnlyCollectionException("List is read only"); + } + + /// <summary> + /// + /// </summary> + /// <param name="otherView"></param> + /// <returns></returns> + public IList<T> Span(IList<T> otherView) + { + GuardedList<T> otherGuardedList = otherView as GuardedList<T>; + if (otherGuardedList == null) + throw new IncompatibleViewException(); + IList<T> span = innerlist.Span(otherGuardedList.innerlist); + if (span == null) + return null; + return new GuardedList<T>(span, underlying ?? otherGuardedList.underlying ?? this, true); + } + + /// <summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// </summary> + public void Reverse() { throw new ReadOnlyCollectionException("List is read only"); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="start"></param> + /// <param name="count"></param> + public void Reverse(int start, int count) + { throw new ReadOnlyCollectionException("List is read only"); } + + + /// <summary> + /// Check if wrapped list is sorted according to the default sorting order + /// for the item type T, as defined by the <see cref="T:C5.Comparer`1"/> class + /// </summary> + /// <exception cref="NotComparableException">if T is not comparable</exception> + /// <returns>True if the list is sorted, else false.</returns> + public bool IsSorted() { return innerlist.IsSorted(Comparer<T>.Default); } + + /// <summary> + /// Check if wrapped list is sorted + /// </summary> + /// <param name="c">The sorting order to use</param> + /// <returns>True if sorted</returns> + public bool IsSorted(SCG.IComparer<T> c) { return innerlist.IsSorted(c); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + public void Sort() + { throw new ReadOnlyCollectionException("List is read only"); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="c"></param> + public void Sort(SCG.IComparer<T> c) + { throw new ReadOnlyCollectionException("List is read only"); } + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + public void Shuffle() + { throw new ReadOnlyCollectionException("List is read only"); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="rnd"></param> + public void Shuffle(Random rnd) + { throw new ReadOnlyCollectionException("List is read only"); } + + #endregion + + #region IIndexed<T> Members + + /// <summary> </summary> + /// <value>A directed collection of the items in the indicated interval of the wrapped collection</value> + public IDirectedCollectionValue<T> this[int start, int end] + { get { return new GuardedDirectedCollectionValue<T>(innerlist[start, end]); } } + + + /// <summary> + /// Find the (first) index of an item in the wrapped collection + /// </summary> + /// <param name="item"></param> + /// <returns></returns> + public int IndexOf(T item) { return innerlist.IndexOf(item); } + + + /// <summary> + /// Find the last index of an item in the wrapped collection + /// </summary> + /// <param name="item"></param> + /// <returns></returns> + public int LastIndexOf(T item) { return innerlist.LastIndexOf(item); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="i"></param> + /// <returns></returns> + public T RemoveAt(int i) + { throw new ReadOnlyCollectionException("List is read only"); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="start"></param> + /// <param name="count"></param> + public void RemoveInterval(int start, int count) + { throw new ReadOnlyCollectionException("List is read only"); } + + #endregion + + #region IDirectedEnumerable<T> Members + + IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() + { return Backwards(); } + + #endregion + + #region IStack<T> Members + + + /// <summary> + /// + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <returns>-</returns> + public void Push(T item) + { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); } + + /// <summary> + /// + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <returns>-</returns> + public T Pop() + { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); } + + #endregion + + #region IQueue<T> Members + + /// <summary> + /// + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <returns>-</returns> + public void Enqueue(T item) + { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); } + + /// <summary> + /// + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <returns>-</returns> + public T Dequeue() + { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); } + + #endregion + + #region IDisposable Members + + /// <summary> + /// Ignore: this may be called by a foreach or using statement. + /// </summary> + public void Dispose() { } + + #endregion + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public override object Clone() + { + return new GuardedList<T>((IList<T>)(innerlist.Clone())); + } + + #region System.Collections.Generic.IList<T> Members + + void System.Collections.Generic.IList<T>.RemoveAt(int index) + { + throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); + } + + void System.Collections.Generic.ICollection<T>.Add(T item) + { + throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); + } + + #endregion + + #region System.Collections.ICollection Members + + bool System.Collections.ICollection.IsSynchronized + { + get { return false; } + } + + [Obsolete] + Object System.Collections.ICollection.SyncRoot + { + get { return innerlist.SyncRoot; } + } + + void System.Collections.ICollection.CopyTo(Array arr, int index) + { + if (index < 0 || index + Count > arr.Length) + throw new ArgumentOutOfRangeException(); + + foreach (T item in this) + arr.SetValue(item, index++); + } + + #endregion + + #region System.Collections.IList Members + + Object System.Collections.IList.this[int index] + { + get { return this[index]; } + set + { + throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); + } + } + + int System.Collections.IList.Add(Object o) + { + throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); + } + + bool System.Collections.IList.Contains(Object o) + { + return Contains((T)o); + } + + int System.Collections.IList.IndexOf(Object o) + { + return Math.Max(-1, IndexOf((T)o)); + } + + void System.Collections.IList.Insert(int index, Object o) + { + throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); + } + + void System.Collections.IList.Remove(Object o) + { + throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); + } + + void System.Collections.IList.RemoveAt(int index) + { + throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); + } + + #endregion + } + + /// <summary> + /// A read-only wrapper for a generic indexable queue (allows indexing). + /// + /// <para>Suitable for wrapping a <see cref="T:C5.CircularQueue`1"/></para> + /// </summary> + /// <typeparam name="T">The item type.</typeparam> + public class GuardedQueue<T> : GuardedDirectedCollectionValue<T>, IQueue<T> + { + #region Fields + + IQueue<T> queue; + + #endregion + + #region Constructor + + /// <summary> + /// Wrap a queue in a read-only wrapper + /// </summary> + /// <param name="queue">The queue</param> + public GuardedQueue(IQueue<T> queue) : base(queue) { this.queue = queue; } + + #endregion + + #region IQueue<T> Members + /// <summary> + /// + /// </summary> + /// <value></value> + public bool AllowsDuplicates { get { return queue.AllowsDuplicates; } } + + /// <summary> + /// Index into the wrapped queue + /// </summary> + /// <param name="i"></param> + /// <returns></returns> + public T this[int i] { get { return queue[i]; } } + + /// <summary> + /// + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <returns>-</returns> + public void Enqueue(T item) + { throw new ReadOnlyCollectionException("Queue cannot be modified through this guard object"); } + + /// <summary> + /// + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <returns>-</returns> + public T Dequeue() + { throw new ReadOnlyCollectionException("Queue cannot be modified through this guard object"); } + + #endregion + } + + /// <summary> + /// A read-only wrapper for a dictionary. + /// + /// <i>Suitable for wrapping a HashDictionary. <see cref="T:C5.HashDictionary`2"/></i> + /// </summary> + public class GuardedDictionary<K, V> : GuardedCollectionValue<KeyValuePair<K, V>>, IDictionary<K, V> + { + #region Fields + + IDictionary<K, V> dict; + + #endregion + + #region Constructor + + /// <summary> + /// Wrap a dictionary in a read-only wrapper + /// </summary> + /// <param name="dict">the dictionary</param> + public GuardedDictionary(IDictionary<K, V> dict) : base(dict) { this.dict = dict; } + + #endregion + + #region IDictionary<K,V> Members + + /// <summary> + /// + /// </summary> + /// <value></value> + public SCG.IEqualityComparer<K> EqualityComparer { get { return dict.EqualityComparer; } } + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a + /// read-only wrappper if used as a setter</exception> + /// <value>Get the value corresponding to a key in the wrapped dictionary</value> + public V this[K key] + { + get { return dict[key]; } + set { throw new ReadOnlyCollectionException(); } + } + + /// <summary> + /// (This is a read-only wrapper) + /// </summary> + /// <value>True</value> + public bool IsReadOnly { get { return true; } } + + + //TODO: guard with a read-only wrapper? Probably so! + /// <summary> </summary> + /// <value>The collection of keys of the wrapped dictionary</value> + public ICollectionValue<K> Keys + { get { return dict.Keys; } } + + + /// <summary> </summary> + /// <value>The collection of values of the wrapped dictionary</value> + public ICollectionValue<V> Values { get { return dict.Values; } } + + /// <summary> + /// + /// </summary> + public virtual Fun<K, V> Fun { get { return delegate(K k) { return this[k]; }; } } + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="key"></param> + /// <param name="val"></param> + public void Add(K key, V val) + { throw new ReadOnlyCollectionException(); } + + /// <summary> + /// + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="items"></param> + public void AddAll<L, W>(SCG.IEnumerable<KeyValuePair<L, W>> items) + where L : K + where W : V + { throw new ReadOnlyCollectionException(); } + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="key"></param> + /// <returns></returns> + public bool Remove(K key) + { throw new ReadOnlyCollectionException(); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="key"></param> + /// <param name="val"></param> + /// <returns></returns> + public bool Remove(K key, out V val) + { throw new ReadOnlyCollectionException(); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + public void Clear() + { throw new ReadOnlyCollectionException(); } + + /// <summary> + /// + /// </summary> + /// <value></value> + public Speed ContainsSpeed { get { return dict.ContainsSpeed; } } + + /// <summary> + /// Check if the wrapped dictionary contains a specific key + /// </summary> + /// <param name="key">The key</param> + /// <returns>True if it does</returns> + public bool Contains(K key) { return dict.Contains(key); } + + /// <summary> + /// + /// </summary> + /// <param name="keys"></param> + /// <returns></returns> + public bool ContainsAll<H>(SCG.IEnumerable<H> keys) where H : K { return dict.ContainsAll(keys); } + + /// <summary> + /// Search for a key in the wrapped dictionary, reporting the value if found + /// </summary> + /// <param name="key">The key</param> + /// <param name="val">On exit: the value if found</param> + /// <returns>True if found</returns> + public bool Find(K key, out V val) { return dict.Find(key, out val); } + + /// <summary> + /// Search for a key in the wrapped dictionary, reporting the value if found + /// </summary> + /// <param name="key">The key</param> + /// <param name="val">On exit: the value if found</param> + /// <returns>True if found</returns> + public bool Find(ref K key, out V val) { return dict.Find(ref key, out val); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="key"></param> + /// <param name="val"></param> + /// <returns></returns> + public bool Update(K key, V val) + { throw new ReadOnlyCollectionException(); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="key"></param> + /// <param name="val"></param> + /// <param name="oldval"></param> + /// <returns></returns> + public bool Update(K key, V val, out V oldval) + { throw new ReadOnlyCollectionException(); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="key"></param> + /// <param name="val"></param> + /// <returns></returns> + public bool FindOrAdd(K key, ref V val) + { throw new ReadOnlyCollectionException(); } + + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="key"></param> + /// <param name="val"></param> + /// <returns></returns> + public bool UpdateOrAdd(K key, V val) + { throw new ReadOnlyCollectionException(); } + + /// <summary> + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="key"></param> + /// <param name="val"></param> + /// <param name="oldval"></param> + /// <returns></returns> + public bool UpdateOrAdd(K key, V val, out V oldval) + { throw new ReadOnlyCollectionException(); } + + + /// <summary> + /// Check the internal consistency of the wrapped dictionary + /// </summary> + /// <returns>True if check passed</returns> + public bool Check() { return dict.Check(); } + + #endregion + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public virtual object Clone() + { + return new GuardedDictionary<K, V>((IDictionary<K, V>)(dict.Clone())); + } + } + + + + /// <summary> + /// A read-only wrapper for a sorted dictionary. + /// + /// <i>Suitable for wrapping a Dictionary. <see cref="T:C5.Dictionary`2"/></i> + /// </summary> + public class GuardedSortedDictionary<K, V> : GuardedDictionary<K, V>, ISortedDictionary<K, V> + { + #region Fields + + ISortedDictionary<K, V> sorteddict; + + #endregion + + #region Constructor + + /// <summary> + /// Wrap a sorted dictionary in a read-only wrapper + /// </summary> + /// <param name="sorteddict">the dictionary</param> + public GuardedSortedDictionary(ISortedDictionary<K, V> sorteddict) + : base(sorteddict) + { this.sorteddict = sorteddict; } + + #endregion + + #region ISortedDictionary<K,V> Members + + /// <summary> + /// The key comparer used by this dictionary. + /// </summary> + /// <value></value> + public SCG.IComparer<K> Comparer { get { return sorteddict.Comparer; } } + + /// <summary> + /// + /// </summary> + /// <value></value> + public new ISorted<K> Keys { get { return null; } } + + /// <summary> + /// Find the entry in the dictionary whose key is the + /// predecessor of the specified key. + /// </summary> + /// <param name="key">The key</param> + /// <param name="res">The predecessor, if any</param> + /// <returns>True if key has a predecessor</returns> + public bool TryPredecessor(K key, out KeyValuePair<K, V> res) + { + return sorteddict.TryPredecessor(key, out res); + } + + /// <summary> + /// Find the entry in the dictionary whose key is the + /// successor of the specified key. + /// </summary> + /// <param name="key">The key</param> + /// <param name="res">The successor, if any</param> + /// <returns>True if the key has a successor</returns> + public bool TrySuccessor(K key, out KeyValuePair<K, V> res) + { + return sorteddict.TrySuccessor(key, out res); + } + + /// <summary> + /// Find the entry in the dictionary whose key is the + /// weak predecessor of the specified key. + /// </summary> + /// <param name="key">The key</param> + /// <param name="res">The predecessor, if any</param> + /// <returns>True if key has a weak predecessor</returns> + public bool TryWeakPredecessor(K key, out KeyValuePair<K, V> res) + { + return sorteddict.TryWeakPredecessor(key, out res); + } + + /// <summary> + /// Find the entry in the dictionary whose key is the + /// weak successor of the specified key. + /// </summary> + /// <param name="key">The key</param> + /// <param name="res">The weak successor, if any</param> + /// <returns>True if the key has a weak successor</returns> + public bool TryWeakSuccessor(K key, out KeyValuePair<K, V> res) + { + return sorteddict.TryWeakSuccessor(key, out res); + } + + /// <summary> + /// Get the entry in the wrapped dictionary whose key is the + /// predecessor of a specified key. + /// </summary> + /// <exception cref="NoSuchItemException"> if no such entry exists </exception> + /// <param name="key">The key</param> + /// <returns>The entry</returns> + public KeyValuePair<K, V> Predecessor(K key) + { return sorteddict.Predecessor(key); } + + /// <summary> + /// Get the entry in the wrapped dictionary whose key is the + /// successor of a specified key. + /// </summary> + /// <exception cref="NoSuchItemException"> if no such entry exists </exception> + /// <param name="key">The key</param> + /// <returns>The entry</returns> + public KeyValuePair<K, V> Successor(K key) + { return sorteddict.Successor(key); } + + + /// <summary> + /// Get the entry in the wrapped dictionary whose key is the + /// weak predecessor of a specified key. + /// </summary> + /// <exception cref="NoSuchItemException"> if no such entry exists </exception> + /// <param name="key">The key</param> + /// <returns>The entry</returns> + public KeyValuePair<K, V> WeakPredecessor(K key) + { return sorteddict.WeakPredecessor(key); } + + + /// <summary> + /// Get the entry in the wrapped dictionary whose key is the + /// weak successor of a specified key. + /// </summary> + /// <exception cref="NoSuchItemException"> if no such entry exists </exception> + /// <param name="key">The key</param> + /// <returns>The entry</returns> + public KeyValuePair<K, V> WeakSuccessor(K key) + { return sorteddict.WeakSuccessor(key); } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public KeyValuePair<K, V> FindMin() + { + return sorteddict.FindMin(); + } + + /// <summary> + /// + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <returns></returns> + public KeyValuePair<K, V> DeleteMin() + { throw new ReadOnlyCollectionException(); } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public KeyValuePair<K, V> FindMax() + { + return sorteddict.FindMax(); + } + + /// <summary> + /// + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <returns></returns> + public KeyValuePair<K, V> DeleteMax() + { throw new ReadOnlyCollectionException(); } + + /// <summary> + /// + /// </summary> + /// <param name="c"></param> + /// <param name="lowEntry"></param> + /// <param name="lowIsValid"></param> + /// <param name="highEntry"></param> + /// <param name="highIsValid"></param> + /// <returns></returns> + public bool Cut(IComparable<K> c, out KeyValuePair<K, V> lowEntry, out bool lowIsValid, out KeyValuePair<K, V> highEntry, out bool highIsValid) + { + return sorteddict.Cut(c, out lowEntry, out lowIsValid, out highEntry, out highIsValid); ; + } + + /// <summary> + /// + /// </summary> + /// <param name="bot"></param> + /// <returns></returns> + public IDirectedEnumerable<KeyValuePair<K, V>> RangeFrom(K bot) + { + return new GuardedDirectedEnumerable<KeyValuePair<K, V>>(sorteddict.RangeFrom(bot)); + } + + /// <summary> + /// + /// </summary> + /// <param name="bot"></param> + /// <param name="top"></param> + /// <returns></returns> + public IDirectedEnumerable<KeyValuePair<K, V>> RangeFromTo(K bot, K top) + { + return new GuardedDirectedEnumerable<KeyValuePair<K, V>>(sorteddict.RangeFromTo(bot, top)); + } + + /// <summary> + /// + /// </summary> + /// <param name="top"></param> + /// <returns></returns> + public IDirectedEnumerable<KeyValuePair<K, V>> RangeTo(K top) + { + return new GuardedDirectedEnumerable<KeyValuePair<K, V>>(sorteddict.RangeTo(top)); + } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public IDirectedCollectionValue<KeyValuePair<K, V>> RangeAll() + { + return new GuardedDirectedCollectionValue<KeyValuePair<K, V>>(sorteddict.RangeAll()); + } + + /// <summary> + /// + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="items"></param> + public void AddSorted(System.Collections.Generic.IEnumerable<KeyValuePair<K, V>> items) + { throw new ReadOnlyCollectionException(); } + + /// <summary> + /// + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="low"></param> + public void RemoveRangeFrom(K low) + { throw new ReadOnlyCollectionException(); } + + /// <summary> + /// + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="low"></param> + /// <param name="hi"></param> + public void RemoveRangeFromTo(K low, K hi) + { throw new ReadOnlyCollectionException(); } + + /// <summary> + /// + /// </summary> + /// <exception cref="ReadOnlyCollectionException"> since this is a read-only wrappper</exception> + /// <param name="hi"></param> + public void RemoveRangeTo(K hi) + { throw new ReadOnlyCollectionException(); } + + #endregion + } + }
\ No newline at end of file diff --git a/mcs/class/Mono.C5/C5/hashing/HashBag.cs b/mcs/class/Mono.C5/C5/hashing/HashBag.cs index a3366b54ae0..ab16a5340ed 100644 --- a/mcs/class/Mono.C5/C5/hashing/HashBag.cs +++ b/mcs/class/Mono.C5/C5/hashing/HashBag.cs @@ -1,688 +1,688 @@ -/*
- Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-
-using System;
-using System.Diagnostics;
-using SCG = System.Collections.Generic;
-
-namespace C5
-{
- /// <summary>
- /// A bag collection based on a hash table of (item,count) pairs.
- /// </summary>
- [Serializable]
- public class HashBag<T> : CollectionBase<T>, ICollection<T>
- {
- #region Fields
- HashSet<KeyValuePair<T, int>> dict;
- #endregion
-
- #region Events
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public override EventTypeEnum ListenableEvents { get { return EventTypeEnum.Basic; } }
-
- #endregion
-
- #region Constructors
- /// <summary>
- /// Create a hash bag with the deafult item equalityComparer.
- /// </summary>
- public HashBag() : this(EqualityComparer<T>.Default) { }
-
- /// <summary>
- /// Create a hash bag with an external item equalityComparer.
- /// </summary>
- /// <param name="itemequalityComparer">The external item equalityComparer.</param>
- public HashBag(SCG.IEqualityComparer<T> itemequalityComparer)
- : base(itemequalityComparer)
- {
- dict = new HashSet<KeyValuePair<T, int>>(new KeyValuePairEqualityComparer<T, int>(itemequalityComparer));
- }
-
- /// <summary>
- /// Create a hash bag with external item equalityComparer, prescribed initial table size and default fill threshold (66%)
- /// </summary>
- /// <param name="capacity">Initial table size (rounded to power of 2, at least 16)</param>
- /// <param name="itemequalityComparer">The external item equalityComparer</param>
- public HashBag(int capacity, SCG.IEqualityComparer<T> itemequalityComparer)
- : base(itemequalityComparer)
- {
- dict = new HashSet<KeyValuePair<T, int>>(capacity, new KeyValuePairEqualityComparer<T, int>(itemequalityComparer));
- }
-
-
- /// <summary>
- /// Create a hash bag with external item equalityComparer, prescribed initial table size and fill threshold.
- /// </summary>
- /// <param name="capacity">Initial table size (rounded to power of 2, at least 16)</param>
- /// <param name="fill">Fill threshold (valid range 10% to 90%)</param>
- /// <param name="itemequalityComparer">The external item equalityComparer</param>
- public HashBag(int capacity, double fill, SCG.IEqualityComparer<T> itemequalityComparer)
- : base(itemequalityComparer)
- {
- dict = new HashSet<KeyValuePair<T, int>>(capacity, fill, new KeyValuePairEqualityComparer<T, int>(itemequalityComparer));
- }
-
- #endregion
-
- #region IEditableCollection<T> Members
-
- /// <summary>
- /// The complexity of the Contains operation
- /// </summary>
- /// <value>Always returns Speed.Constant</value>
- [Tested]
- public virtual Speed ContainsSpeed { [Tested]get { return Speed.Constant; } }
-
- /// <summary>
- /// Check if an item is in the bag
- /// </summary>
- /// <param name="item">The item to look for</param>
- /// <returns>True if bag contains item</returns>
- [Tested]
- public virtual bool Contains(T item)
- { return dict.Contains(new KeyValuePair<T, int>(item, 0)); }
-
-
- /// <summary>
- /// Check if an item (collection equal to a given one) is in the bag and
- /// if so report the actual item object found.
- /// </summary>
- /// <param name="item">On entry, the item to look for.
- /// On exit the item found, if any</param>
- /// <returns>True if bag contains item</returns>
- [Tested]
- public virtual bool Find(ref T item)
- {
- KeyValuePair<T, int> p = new KeyValuePair<T, int>(item, 0);
-
- if (dict.Find(ref p))
- {
- item = p.Key;
- return true;
- }
-
- return false;
- }
-
-
- /// <summary>
- /// Check if an item (collection equal to a given one) is in the bag and
- /// if so replace the item object in the bag with the supplied one.
- /// </summary>
- /// <param name="item">The item object to update with</param>
- /// <returns>True if item was found (and updated)</returns>
- [Tested]
- public virtual bool Update(T item)
- { T olditem = default(T); return Update(item, out olditem); }
-
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <param name="olditem"></param>
- /// <returns></returns>
- public virtual bool Update(T item, out T olditem)
- {
- KeyValuePair<T, int> p = new KeyValuePair<T, int>(item, 0);
-
- updatecheck();
-
- //Note: we cannot just do dict.Update: we have to lookup the count before we
- //know what to update with. There is of course a way around if we use the
- //implementation of hashset -which we do not want to do.
- //The hashbag is moreover mainly a proof of concept
- if (dict.Find(ref p))
- {
- olditem = p.Key;
- p.Key = item;
- dict.Update(p);
- if (ActiveEvents != 0)
- raiseForUpdate(item, olditem, p.Value);
- return true;
- }
-
- olditem = default(T);
- return false;
- }
-
-
- /// <summary>
- /// Check if an item (collection equal to a given one) is in the bag.
- /// If found, report the actual item object in the bag,
- /// else add the supplied one.
- /// </summary>
- /// <param name="item">On entry, the item to look for or add.
- /// On exit the actual object found, if any.</param>
- /// <returns>True if item was found</returns>
- [Tested]
- public virtual bool FindOrAdd(ref T item)
- {
- updatecheck();
- if (Find(ref item))
- return true;
-
- Add(item);
- return false;
- }
-
-
- /// <summary>
- /// Check if an item (collection equal to a supplied one) is in the bag and
- /// if so replace the item object in the set with the supplied one; else
- /// add the supplied one.
- /// </summary>
- /// <param name="item">The item to look for and update or add</param>
- /// <returns>True if item was updated</returns>
- [Tested]
- public virtual bool UpdateOrAdd(T item)
- {
- updatecheck();
- if (Update(item))
- return true;
-
- Add(item);
- return false;
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <param name="olditem"></param>
- /// <returns></returns>
- public virtual bool UpdateOrAdd(T item, out T olditem)
- {
- updatecheck();
- if (Update(item, out olditem))
- return true;
-
- Add(item);
- return false;
- }
-
- /// <summary>
- /// Remove one copy af an item from the bag
- /// </summary>
- /// <param name="item">The item to remove</param>
- /// <returns>True if item was (found and) removed </returns>
- [Tested]
- public virtual bool Remove(T item)
- {
- KeyValuePair<T, int> p = new KeyValuePair<T, int>(item, 0);
-
- updatecheck();
- if (dict.Find(ref p))
- {
- size--;
- if (p.Value == 1)
- dict.Remove(p);
- else
- {
- p.Value--;
- dict.Update(p);
- }
- if (ActiveEvents != 0)
- raiseForRemove(p.Key);
- return true;
- }
-
- return false;
- }
-
-
- /// <summary>
- /// Remove one copy of an item from the bag, reporting the actual matching item object.
- /// </summary>
- /// <param name="item">The value to remove.</param>
- /// <param name="removeditem">The removed value.</param>
- /// <returns>True if item was found.</returns>
- [Tested]
- public virtual bool Remove(T item, out T removeditem)
- {
- updatecheck();
- KeyValuePair<T, int> p = new KeyValuePair<T, int>(item, 0);
- if (dict.Find(ref p))
- {
- removeditem = p.Key;
- size--;
- if (p.Value == 1)
- dict.Remove(p);
- else
- {
- p.Value--;
- dict.Update(p);
- }
- if (ActiveEvents != 0)
- raiseForRemove(removeditem);
-
- return true;
- }
-
- removeditem = default(T);
- return false;
- }
-
- /// <summary>
- /// Remove all items in a supplied collection from this bag, counting multiplicities.
- /// </summary>
- /// <typeparam name="U"></typeparam>
- /// <param name="items">The items to remove.</param>
- [Tested]
- public virtual void RemoveAll<U>(SCG.IEnumerable<U> items) where U : T
- {
-#warning Improve if items is a counting bag
- updatecheck();
- bool mustRaise = (ActiveEvents & (EventTypeEnum.Changed | EventTypeEnum.Removed)) != 0;
- RaiseForRemoveAllHandler raiseHandler = mustRaise ? new RaiseForRemoveAllHandler(this) : null;
- foreach (U item in items)
- {
- KeyValuePair<T, int> p = new KeyValuePair<T, int>(item, 0);
- if (dict.Find(ref p))
- {
- size--;
- if (p.Value == 1)
- dict.Remove(p);
- else
- {
- p.Value--;
- dict.Update(p);
- }
- if (mustRaise)
- raiseHandler.Remove(p.Key);
- }
- }
- if (mustRaise)
- raiseHandler.Raise();
- }
-
- /// <summary>
- /// Remove all items from the bag, resetting internal table to initial size.
- /// </summary>
- [Tested]
- public virtual void Clear()
- {
- updatecheck();
- if (size == 0)
- return;
- dict.Clear();
- int oldsize = size;
- size = 0;
- if ((ActiveEvents & EventTypeEnum.Cleared) != 0)
- raiseCollectionCleared(true, oldsize);
- if ((ActiveEvents & EventTypeEnum.Changed) != 0)
- raiseCollectionChanged();
- }
-
-
- /// <summary>
- /// Remove all items *not* in a supplied collection from this bag,
- /// counting multiplicities.
- /// </summary>
- /// <typeparam name="U"></typeparam>
- /// <param name="items">The items to retain</param>
- [Tested]
- public virtual void RetainAll<U>(SCG.IEnumerable<U> items) where U : T
- {
- updatecheck();
-
- HashBag<T> res = new HashBag<T>(itemequalityComparer);
-
- foreach (U item in items)
- {
- KeyValuePair<T, int> p = new KeyValuePair<T, int>(item);
- if (dict.Find(ref p))
- {
- KeyValuePair<T, int> q = p;
- if (res.dict.Find(ref q))
- {
- if (q.Value < p.Value)
- {
- q.Value++;
- res.dict.Update(q);
- res.size++;
- }
- }
- else
- {
- q.Value = 1;
- res.dict.Add(q);
- res.size++;
- }
- }
- }
-
- if (size == res.size)
- return;
-
- CircularQueue<T> wasRemoved = null;
- if ((ActiveEvents & EventTypeEnum.Removed) != 0)
- {
- wasRemoved = new CircularQueue<T>();
- foreach (KeyValuePair<T, int> p in dict)
- {
- int removed = p.Value - res.ContainsCount(p.Key);
- if (removed > 0)
-#warning We could send bag events here easily using a CircularQueue of (should?)
- for (int i = 0; i < removed; i++)
- wasRemoved.Enqueue(p.Key);
- }
- }
- dict = res.dict;
- size = res.size;
-
- if ((ActiveEvents & EventTypeEnum.Removed) != 0)
- raiseForRemoveAll(wasRemoved);
- else if ((ActiveEvents & EventTypeEnum.Changed) != 0)
- raiseCollectionChanged();
- }
-
- /// <summary>
- /// Check if all items in a supplied collection is in this bag
- /// (counting multiplicities).
- /// </summary>
- /// <param name="items">The items to look for.</param>
- /// <typeparam name="U"></typeparam>
- /// <returns>True if all items are found.</returns>
- [Tested]
- public virtual bool ContainsAll<U>(SCG.IEnumerable<U> items) where U : T
- {
- HashBag<T> res = new HashBag<T>(itemequalityComparer);
-
- foreach (T item in items)
- if (res.ContainsCount(item) < ContainsCount(item))
- res.Add(item);
- else
- return false;
-
- return true;
- }
-
-
- /// <summary>
- /// Create an array containing all items in this bag (in enumeration order).
- /// </summary>
- /// <returns>The array</returns>
- [Tested]
- public override T[] ToArray()
- {
- T[] res = new T[size];
- int ind = 0;
-
- foreach (KeyValuePair<T, int> p in dict)
- for (int i = 0; i < p.Value; i++)
- res[ind++] = p.Key;
-
- return res;
- }
-
-
- /// <summary>
- /// Count the number of times an item is in this set.
- /// </summary>
- /// <param name="item">The item to look for.</param>
- /// <returns>The count</returns>
- [Tested]
- public virtual int ContainsCount(T item)
- {
- KeyValuePair<T, int> p = new KeyValuePair<T, int>(item, 0);
-
- if (dict.Find(ref p))
- return p.Value;
-
- return 0;
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public virtual ICollectionValue<T> UniqueItems() { return new DropMultiplicity<T>(dict); }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public virtual ICollectionValue<KeyValuePair<T, int>> ItemMultiplicities()
- {
- return new GuardedCollectionValue<KeyValuePair<T, int>>(dict);
- }
-
- /// <summary>
- /// Remove all copies of item from this set.
- /// </summary>
- /// <param name="item">The item to remove</param>
- [Tested]
- public virtual void RemoveAllCopies(T item)
- {
- updatecheck();
-
- KeyValuePair<T, int> p = new KeyValuePair<T, int>(item, 0);
-
- if (dict.Find(ref p))
- {
- size -= p.Value;
- dict.Remove(p);
- if ((ActiveEvents & EventTypeEnum.Removed) != 0)
- raiseItemsRemoved(p.Key, p.Value);
- if ((ActiveEvents & EventTypeEnum.Changed) != 0)
- raiseCollectionChanged();
- }
- }
-
- #endregion
-
- #region ICollection<T> Members
-
-
- /// <summary>
- /// Copy the items of this bag to part of an array.
- /// <exception cref="ArgumentOutOfRangeException"/> if i is negative.
- /// <exception cref="ArgumentException"/> if the array does not have room for the items.
- /// </summary>
- /// <param name="array">The array to copy to</param>
- /// <param name="index">The starting index.</param>
- [Tested]
- public override void CopyTo(T[] array, int index)
- {
- if (index < 0 || index + Count > array.Length)
- throw new ArgumentOutOfRangeException();
-
- foreach (KeyValuePair<T, int> p in dict)
- for (int j = 0; j < p.Value; j++)
- array[index++] = p.Key;
- }
-
- #endregion
-
- #region IExtensible<T> Members
-
- /// <summary>
- /// Report if this is a set collection.
- /// </summary>
- /// <value>Always true</value>
- [Tested]
- public virtual bool AllowsDuplicates { [Tested] get { return true; } }
-
- /// <summary>
- /// By convention this is true for any collection with set semantics.
- /// </summary>
- /// <value>True if only one representative of a group of equal items
- /// is kept in the collection together with the total count.</value>
- public virtual bool DuplicatesByCounting { get { return true; } }
-
- /// <summary>
- /// Add an item to this bag.
- /// </summary>
- /// <param name="item">The item to add.</param>
- /// <returns>Always true</returns>
- [Tested]
- public virtual bool Add(T item)
- {
- updatecheck();
- add(ref item);
- if (ActiveEvents != 0)
- raiseForAdd(item);
- return true;
- }
-
- /// <summary>
- /// Add an item to this bag.
- /// </summary>
- /// <param name="item">The item to add.</param>
- [Tested]
- void SCG.ICollection<T>.Add(T item)
- {
- Add(item);
- }
-
- private void add(ref T item)
- {
- KeyValuePair<T, int> p = new KeyValuePair<T, int>(item, 1);
- if (dict.Find(ref p))
- {
- p.Value++;
- dict.Update(p);
- item = p.Key;
- }
- else
- dict.Add(p);
- size++;
- }
-
- /// <summary>
- /// Add the elements from another collection with a more specialized item type
- /// to this collection.
- /// </summary>
- /// <typeparam name="U">The type of items to add</typeparam>
- /// <param name="items">The items to add</param>
- public virtual void AddAll<U>(SCG.IEnumerable<U> items) where U : T
- {
- updatecheck();
-#warning We could easily raise bag events
- bool mustRaiseAdded = (ActiveEvents & EventTypeEnum.Added) != 0;
- CircularQueue<T> wasAdded = mustRaiseAdded ? new CircularQueue<T>() : null;
- bool wasChanged = false;
- foreach (T item in items)
- {
- T jtem = item;
- add(ref jtem);
- wasChanged = true;
- if (mustRaiseAdded)
- wasAdded.Enqueue(jtem);
- }
- if (!wasChanged)
- return;
- if (mustRaiseAdded)
- foreach (T item in wasAdded)
- raiseItemsAdded(item, 1);
- if ((ActiveEvents & EventTypeEnum.Changed) != 0)
- raiseCollectionChanged();
- }
-
- #endregion
-
- #region IEnumerable<T> Members
-
-
- /// <summary>
- /// Choose some item of this collection.
- /// </summary>
- /// <exception cref="NoSuchItemException">if collection is empty.</exception>
- /// <returns></returns>
- public override T Choose()
- {
- return dict.Choose().Key;
- }
-
- /// <summary>
- /// Create an enumerator for this bag.
- /// </summary>
- /// <returns>The enumerator</returns>
- [Tested]
- public override SCG.IEnumerator<T> GetEnumerator()
- {
- int left;
- int mystamp = stamp;
-
- foreach (KeyValuePair<T, int> p in dict)
- {
- left = p.Value;
- while (left > 0)
- {
- if (mystamp != stamp)
- throw new CollectionModifiedException();
-
- left--;
- yield return p.Key;
- }
- }
- }
- #endregion
-
- #region ICloneable Members
-
- /// <summary>
- /// Make a shallow copy of this HashBag.
- /// </summary>
- /// <returns></returns>
- public virtual object Clone()
- {
- //TODO: make sure this
- HashBag<T> clone = new HashBag<T>(dict.Count > 0 ? dict.Count : 1, itemequalityComparer);
- //TODO: make sure this really adds in the counting bag way!
- clone.AddAll(this);
- return clone;
- }
-
- #endregion
-
-
- #region Diagnostics
- /// <summary>
- /// Test internal structure of data (invariants)
- /// </summary>
- /// <returns>True if pass</returns>
- [Tested]
- public virtual bool Check()
- {
- bool retval = dict.Check();
- int count = 0;
-
- foreach (KeyValuePair<T, int> p in dict)
- count += p.Value;
-
- if (count != size)
- {
- Console.WriteLine("count({0}) != size({1})", count, size);
- retval = false;
- }
-
- return retval;
- }
- #endregion
- }
-}
+/* + Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +using System; +using System.Diagnostics; +using SCG = System.Collections.Generic; + +namespace C5 +{ + /// <summary> + /// A bag collection based on a hash table of (item,count) pairs. + /// </summary> + [Serializable] + public class HashBag<T> : CollectionBase<T>, ICollection<T> + { + #region Fields + HashSet<KeyValuePair<T, int>> dict; + #endregion + + #region Events + + /// <summary> + /// + /// </summary> + /// <value></value> + public override EventTypeEnum ListenableEvents { get { return EventTypeEnum.Basic; } } + + #endregion + + #region Constructors + /// <summary> + /// Create a hash bag with the deafult item equalityComparer. + /// </summary> + public HashBag() : this(EqualityComparer<T>.Default) { } + + /// <summary> + /// Create a hash bag with an external item equalityComparer. + /// </summary> + /// <param name="itemequalityComparer">The external item equalityComparer.</param> + public HashBag(SCG.IEqualityComparer<T> itemequalityComparer) + : base(itemequalityComparer) + { + dict = new HashSet<KeyValuePair<T, int>>(new KeyValuePairEqualityComparer<T, int>(itemequalityComparer)); + } + + /// <summary> + /// Create a hash bag with external item equalityComparer, prescribed initial table size and default fill threshold (66%) + /// </summary> + /// <param name="capacity">Initial table size (rounded to power of 2, at least 16)</param> + /// <param name="itemequalityComparer">The external item equalityComparer</param> + public HashBag(int capacity, SCG.IEqualityComparer<T> itemequalityComparer) + : base(itemequalityComparer) + { + dict = new HashSet<KeyValuePair<T, int>>(capacity, new KeyValuePairEqualityComparer<T, int>(itemequalityComparer)); + } + + + /// <summary> + /// Create a hash bag with external item equalityComparer, prescribed initial table size and fill threshold. + /// </summary> + /// <param name="capacity">Initial table size (rounded to power of 2, at least 16)</param> + /// <param name="fill">Fill threshold (valid range 10% to 90%)</param> + /// <param name="itemequalityComparer">The external item equalityComparer</param> + public HashBag(int capacity, double fill, SCG.IEqualityComparer<T> itemequalityComparer) + : base(itemequalityComparer) + { + dict = new HashSet<KeyValuePair<T, int>>(capacity, fill, new KeyValuePairEqualityComparer<T, int>(itemequalityComparer)); + } + + #endregion + + #region IEditableCollection<T> Members + + /// <summary> + /// The complexity of the Contains operation + /// </summary> + /// <value>Always returns Speed.Constant</value> + [Tested] + public virtual Speed ContainsSpeed { [Tested]get { return Speed.Constant; } } + + /// <summary> + /// Check if an item is in the bag + /// </summary> + /// <param name="item">The item to look for</param> + /// <returns>True if bag contains item</returns> + [Tested] + public virtual bool Contains(T item) + { return dict.Contains(new KeyValuePair<T, int>(item, 0)); } + + + /// <summary> + /// Check if an item (collection equal to a given one) is in the bag and + /// if so report the actual item object found. + /// </summary> + /// <param name="item">On entry, the item to look for. + /// On exit the item found, if any</param> + /// <returns>True if bag contains item</returns> + [Tested] + public virtual bool Find(ref T item) + { + KeyValuePair<T, int> p = new KeyValuePair<T, int>(item, 0); + + if (dict.Find(ref p)) + { + item = p.Key; + return true; + } + + return false; + } + + + /// <summary> + /// Check if an item (collection equal to a given one) is in the bag and + /// if so replace the item object in the bag with the supplied one. + /// </summary> + /// <param name="item">The item object to update with</param> + /// <returns>True if item was found (and updated)</returns> + [Tested] + public virtual bool Update(T item) + { T olditem = default(T); return Update(item, out olditem); } + + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <param name="olditem"></param> + /// <returns></returns> + public virtual bool Update(T item, out T olditem) + { + KeyValuePair<T, int> p = new KeyValuePair<T, int>(item, 0); + + updatecheck(); + + //Note: we cannot just do dict.Update: we have to lookup the count before we + //know what to update with. There is of course a way around if we use the + //implementation of hashset -which we do not want to do. + //The hashbag is moreover mainly a proof of concept + if (dict.Find(ref p)) + { + olditem = p.Key; + p.Key = item; + dict.Update(p); + if (ActiveEvents != 0) + raiseForUpdate(item, olditem, p.Value); + return true; + } + + olditem = default(T); + return false; + } + + + /// <summary> + /// Check if an item (collection equal to a given one) is in the bag. + /// If found, report the actual item object in the bag, + /// else add the supplied one. + /// </summary> + /// <param name="item">On entry, the item to look for or add. + /// On exit the actual object found, if any.</param> + /// <returns>True if item was found</returns> + [Tested] + public virtual bool FindOrAdd(ref T item) + { + updatecheck(); + if (Find(ref item)) + return true; + + Add(item); + return false; + } + + + /// <summary> + /// Check if an item (collection equal to a supplied one) is in the bag and + /// if so replace the item object in the set with the supplied one; else + /// add the supplied one. + /// </summary> + /// <param name="item">The item to look for and update or add</param> + /// <returns>True if item was updated</returns> + [Tested] + public virtual bool UpdateOrAdd(T item) + { + updatecheck(); + if (Update(item)) + return true; + + Add(item); + return false; + } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <param name="olditem"></param> + /// <returns></returns> + public virtual bool UpdateOrAdd(T item, out T olditem) + { + updatecheck(); + if (Update(item, out olditem)) + return true; + + Add(item); + return false; + } + + /// <summary> + /// Remove one copy af an item from the bag + /// </summary> + /// <param name="item">The item to remove</param> + /// <returns>True if item was (found and) removed </returns> + [Tested] + public virtual bool Remove(T item) + { + KeyValuePair<T, int> p = new KeyValuePair<T, int>(item, 0); + + updatecheck(); + if (dict.Find(ref p)) + { + size--; + if (p.Value == 1) + dict.Remove(p); + else + { + p.Value--; + dict.Update(p); + } + if (ActiveEvents != 0) + raiseForRemove(p.Key); + return true; + } + + return false; + } + + + /// <summary> + /// Remove one copy of an item from the bag, reporting the actual matching item object. + /// </summary> + /// <param name="item">The value to remove.</param> + /// <param name="removeditem">The removed value.</param> + /// <returns>True if item was found.</returns> + [Tested] + public virtual bool Remove(T item, out T removeditem) + { + updatecheck(); + KeyValuePair<T, int> p = new KeyValuePair<T, int>(item, 0); + if (dict.Find(ref p)) + { + removeditem = p.Key; + size--; + if (p.Value == 1) + dict.Remove(p); + else + { + p.Value--; + dict.Update(p); + } + if (ActiveEvents != 0) + raiseForRemove(removeditem); + + return true; + } + + removeditem = default(T); + return false; + } + + /// <summary> + /// Remove all items in a supplied collection from this bag, counting multiplicities. + /// </summary> + /// <typeparam name="U"></typeparam> + /// <param name="items">The items to remove.</param> + [Tested] + public virtual void RemoveAll<U>(SCG.IEnumerable<U> items) where U : T + { +#warning Improve if items is a counting bag + updatecheck(); + bool mustRaise = (ActiveEvents & (EventTypeEnum.Changed | EventTypeEnum.Removed)) != 0; + RaiseForRemoveAllHandler raiseHandler = mustRaise ? new RaiseForRemoveAllHandler(this) : null; + foreach (U item in items) + { + KeyValuePair<T, int> p = new KeyValuePair<T, int>(item, 0); + if (dict.Find(ref p)) + { + size--; + if (p.Value == 1) + dict.Remove(p); + else + { + p.Value--; + dict.Update(p); + } + if (mustRaise) + raiseHandler.Remove(p.Key); + } + } + if (mustRaise) + raiseHandler.Raise(); + } + + /// <summary> + /// Remove all items from the bag, resetting internal table to initial size. + /// </summary> + [Tested] + public virtual void Clear() + { + updatecheck(); + if (size == 0) + return; + dict.Clear(); + int oldsize = size; + size = 0; + if ((ActiveEvents & EventTypeEnum.Cleared) != 0) + raiseCollectionCleared(true, oldsize); + if ((ActiveEvents & EventTypeEnum.Changed) != 0) + raiseCollectionChanged(); + } + + + /// <summary> + /// Remove all items *not* in a supplied collection from this bag, + /// counting multiplicities. + /// </summary> + /// <typeparam name="U"></typeparam> + /// <param name="items">The items to retain</param> + [Tested] + public virtual void RetainAll<U>(SCG.IEnumerable<U> items) where U : T + { + updatecheck(); + + HashBag<T> res = new HashBag<T>(itemequalityComparer); + + foreach (U item in items) + { + KeyValuePair<T, int> p = new KeyValuePair<T, int>(item); + if (dict.Find(ref p)) + { + KeyValuePair<T, int> q = p; + if (res.dict.Find(ref q)) + { + if (q.Value < p.Value) + { + q.Value++; + res.dict.Update(q); + res.size++; + } + } + else + { + q.Value = 1; + res.dict.Add(q); + res.size++; + } + } + } + + if (size == res.size) + return; + + CircularQueue<T> wasRemoved = null; + if ((ActiveEvents & EventTypeEnum.Removed) != 0) + { + wasRemoved = new CircularQueue<T>(); + foreach (KeyValuePair<T, int> p in dict) + { + int removed = p.Value - res.ContainsCount(p.Key); + if (removed > 0) +#warning We could send bag events here easily using a CircularQueue of (should?) + for (int i = 0; i < removed; i++) + wasRemoved.Enqueue(p.Key); + } + } + dict = res.dict; + size = res.size; + + if ((ActiveEvents & EventTypeEnum.Removed) != 0) + raiseForRemoveAll(wasRemoved); + else if ((ActiveEvents & EventTypeEnum.Changed) != 0) + raiseCollectionChanged(); + } + + /// <summary> + /// Check if all items in a supplied collection is in this bag + /// (counting multiplicities). + /// </summary> + /// <param name="items">The items to look for.</param> + /// <typeparam name="U"></typeparam> + /// <returns>True if all items are found.</returns> + [Tested] + public virtual bool ContainsAll<U>(SCG.IEnumerable<U> items) where U : T + { + HashBag<T> res = new HashBag<T>(itemequalityComparer); + + foreach (T item in items) + if (res.ContainsCount(item) < ContainsCount(item)) + res.Add(item); + else + return false; + + return true; + } + + + /// <summary> + /// Create an array containing all items in this bag (in enumeration order). + /// </summary> + /// <returns>The array</returns> + [Tested] + public override T[] ToArray() + { + T[] res = new T[size]; + int ind = 0; + + foreach (KeyValuePair<T, int> p in dict) + for (int i = 0; i < p.Value; i++) + res[ind++] = p.Key; + + return res; + } + + + /// <summary> + /// Count the number of times an item is in this set. + /// </summary> + /// <param name="item">The item to look for.</param> + /// <returns>The count</returns> + [Tested] + public virtual int ContainsCount(T item) + { + KeyValuePair<T, int> p = new KeyValuePair<T, int>(item, 0); + + if (dict.Find(ref p)) + return p.Value; + + return 0; + } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public virtual ICollectionValue<T> UniqueItems() { return new DropMultiplicity<T>(dict); } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public virtual ICollectionValue<KeyValuePair<T, int>> ItemMultiplicities() + { + return new GuardedCollectionValue<KeyValuePair<T, int>>(dict); + } + + /// <summary> + /// Remove all copies of item from this set. + /// </summary> + /// <param name="item">The item to remove</param> + [Tested] + public virtual void RemoveAllCopies(T item) + { + updatecheck(); + + KeyValuePair<T, int> p = new KeyValuePair<T, int>(item, 0); + + if (dict.Find(ref p)) + { + size -= p.Value; + dict.Remove(p); + if ((ActiveEvents & EventTypeEnum.Removed) != 0) + raiseItemsRemoved(p.Key, p.Value); + if ((ActiveEvents & EventTypeEnum.Changed) != 0) + raiseCollectionChanged(); + } + } + + #endregion + + #region ICollection<T> Members + + + /// <summary> + /// Copy the items of this bag to part of an array. + /// <exception cref="ArgumentOutOfRangeException"/> if i is negative. + /// <exception cref="ArgumentException"/> if the array does not have room for the items. + /// </summary> + /// <param name="array">The array to copy to</param> + /// <param name="index">The starting index.</param> + [Tested] + public override void CopyTo(T[] array, int index) + { + if (index < 0 || index + Count > array.Length) + throw new ArgumentOutOfRangeException(); + + foreach (KeyValuePair<T, int> p in dict) + for (int j = 0; j < p.Value; j++) + array[index++] = p.Key; + } + + #endregion + + #region IExtensible<T> Members + + /// <summary> + /// Report if this is a set collection. + /// </summary> + /// <value>Always true</value> + [Tested] + public virtual bool AllowsDuplicates { [Tested] get { return true; } } + + /// <summary> + /// By convention this is true for any collection with set semantics. + /// </summary> + /// <value>True if only one representative of a group of equal items + /// is kept in the collection together with the total count.</value> + public virtual bool DuplicatesByCounting { get { return true; } } + + /// <summary> + /// Add an item to this bag. + /// </summary> + /// <param name="item">The item to add.</param> + /// <returns>Always true</returns> + [Tested] + public virtual bool Add(T item) + { + updatecheck(); + add(ref item); + if (ActiveEvents != 0) + raiseForAdd(item); + return true; + } + + /// <summary> + /// Add an item to this bag. + /// </summary> + /// <param name="item">The item to add.</param> + [Tested] + void SCG.ICollection<T>.Add(T item) + { + Add(item); + } + + private void add(ref T item) + { + KeyValuePair<T, int> p = new KeyValuePair<T, int>(item, 1); + if (dict.Find(ref p)) + { + p.Value++; + dict.Update(p); + item = p.Key; + } + else + dict.Add(p); + size++; + } + + /// <summary> + /// Add the elements from another collection with a more specialized item type + /// to this collection. + /// </summary> + /// <typeparam name="U">The type of items to add</typeparam> + /// <param name="items">The items to add</param> + public virtual void AddAll<U>(SCG.IEnumerable<U> items) where U : T + { + updatecheck(); +#warning We could easily raise bag events + bool mustRaiseAdded = (ActiveEvents & EventTypeEnum.Added) != 0; + CircularQueue<T> wasAdded = mustRaiseAdded ? new CircularQueue<T>() : null; + bool wasChanged = false; + foreach (T item in items) + { + T jtem = item; + add(ref jtem); + wasChanged = true; + if (mustRaiseAdded) + wasAdded.Enqueue(jtem); + } + if (!wasChanged) + return; + if (mustRaiseAdded) + foreach (T item in wasAdded) + raiseItemsAdded(item, 1); + if ((ActiveEvents & EventTypeEnum.Changed) != 0) + raiseCollectionChanged(); + } + + #endregion + + #region IEnumerable<T> Members + + + /// <summary> + /// Choose some item of this collection. + /// </summary> + /// <exception cref="NoSuchItemException">if collection is empty.</exception> + /// <returns></returns> + public override T Choose() + { + return dict.Choose().Key; + } + + /// <summary> + /// Create an enumerator for this bag. + /// </summary> + /// <returns>The enumerator</returns> + [Tested] + public override SCG.IEnumerator<T> GetEnumerator() + { + int left; + int mystamp = stamp; + + foreach (KeyValuePair<T, int> p in dict) + { + left = p.Value; + while (left > 0) + { + if (mystamp != stamp) + throw new CollectionModifiedException(); + + left--; + yield return p.Key; + } + } + } + #endregion + + #region ICloneable Members + + /// <summary> + /// Make a shallow copy of this HashBag. + /// </summary> + /// <returns></returns> + public virtual object Clone() + { + //TODO: make sure this + HashBag<T> clone = new HashBag<T>(dict.Count > 0 ? dict.Count : 1, itemequalityComparer); + //TODO: make sure this really adds in the counting bag way! + clone.AddAll(this); + return clone; + } + + #endregion + + + #region Diagnostics + /// <summary> + /// Test internal structure of data (invariants) + /// </summary> + /// <returns>True if pass</returns> + [Tested] + public virtual bool Check() + { + bool retval = dict.Check(); + int count = 0; + + foreach (KeyValuePair<T, int> p in dict) + count += p.Value; + + if (count != size) + { + Console.WriteLine("count({0}) != size({1})", count, size); + retval = false; + } + + return retval; + } + #endregion + } +} diff --git a/mcs/class/Mono.C5/C5/hashing/HashDictionary.cs b/mcs/class/Mono.C5/C5/hashing/HashDictionary.cs index 84adde2c458..36eedce7346 100644 --- a/mcs/class/Mono.C5/C5/hashing/HashDictionary.cs +++ b/mcs/class/Mono.C5/C5/hashing/HashDictionary.cs @@ -1,77 +1,77 @@ -/*
- Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-
-using System;
-using System.Diagnostics;
-using SCG = System.Collections.Generic;
-
-namespace C5
-{
- /// <summary>
- /// A generic dictionary class based on a hash set class <see cref="T:C5.HashSet`1"/>.
- /// </summary>
- [Serializable]
- public class HashDictionary<K, V> : DictionaryBase<K, V>, IDictionary<K, V>
- {
- /// <summary>
- /// Create a hash dictionary using a default equalityComparer for the keys.
- /// Initial capacity of internal table will be 16 entries and threshold for
- /// expansion is 66% fill.
- /// </summary>
- public HashDictionary() : this(EqualityComparer<K>.Default) { }
-
- /// <summary>
- /// Create a hash dictionary using a custom equalityComparer for the keys.
- /// Initial capacity of internal table will be 16 entries and threshold for
- /// expansion is 66% fill.
- /// </summary>
- /// <param name="keyequalityComparer">The external key equalityComparer</param>
- public HashDictionary(SCG.IEqualityComparer<K> keyequalityComparer) : base(keyequalityComparer)
- {
- pairs = new HashSet<KeyValuePair<K, V>>(new KeyValuePairEqualityComparer<K, V>(keyequalityComparer));
- }
-
- /// <summary>
- /// Create a hash dictionary using a custom equalityComparer and prescribing the
- /// initial size of the dictionary and a non-default threshold for internal table expansion.
- /// </summary>
- /// <param name="capacity">The initial capacity. Will be rounded upwards to nearest
- /// power of 2, at least 16.</param>
- /// <param name="fill">The expansion threshold. Must be between 10% and 90%.</param>
- /// <param name="keyequalityComparer">The external key equalityComparer</param>
- public HashDictionary(int capacity, double fill, SCG.IEqualityComparer<K> keyequalityComparer): base(keyequalityComparer)
- {
- pairs = new HashSet<KeyValuePair<K, V>>(capacity, fill, new KeyValuePairEqualityComparer<K, V>(keyequalityComparer));
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public override object Clone()
- {
- HashDictionary<K, V> clone = new HashDictionary<K, V>(EqualityComparer);
- clone.pairs.AddAll(pairs);
- return clone;
- }
-
- }
+/* + Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +using System; +using System.Diagnostics; +using SCG = System.Collections.Generic; + +namespace C5 +{ + /// <summary> + /// A generic dictionary class based on a hash set class <see cref="T:C5.HashSet`1"/>. + /// </summary> + [Serializable] + public class HashDictionary<K, V> : DictionaryBase<K, V>, IDictionary<K, V> + { + /// <summary> + /// Create a hash dictionary using a default equalityComparer for the keys. + /// Initial capacity of internal table will be 16 entries and threshold for + /// expansion is 66% fill. + /// </summary> + public HashDictionary() : this(EqualityComparer<K>.Default) { } + + /// <summary> + /// Create a hash dictionary using a custom equalityComparer for the keys. + /// Initial capacity of internal table will be 16 entries and threshold for + /// expansion is 66% fill. + /// </summary> + /// <param name="keyequalityComparer">The external key equalityComparer</param> + public HashDictionary(SCG.IEqualityComparer<K> keyequalityComparer) : base(keyequalityComparer) + { + pairs = new HashSet<KeyValuePair<K, V>>(new KeyValuePairEqualityComparer<K, V>(keyequalityComparer)); + } + + /// <summary> + /// Create a hash dictionary using a custom equalityComparer and prescribing the + /// initial size of the dictionary and a non-default threshold for internal table expansion. + /// </summary> + /// <param name="capacity">The initial capacity. Will be rounded upwards to nearest + /// power of 2, at least 16.</param> + /// <param name="fill">The expansion threshold. Must be between 10% and 90%.</param> + /// <param name="keyequalityComparer">The external key equalityComparer</param> + public HashDictionary(int capacity, double fill, SCG.IEqualityComparer<K> keyequalityComparer): base(keyequalityComparer) + { + pairs = new HashSet<KeyValuePair<K, V>>(capacity, fill, new KeyValuePairEqualityComparer<K, V>(keyequalityComparer)); + } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public override object Clone() + { + HashDictionary<K, V> clone = new HashDictionary<K, V>(EqualityComparer); + clone.pairs.AddAll(pairs); + return clone; + } + + } }
\ No newline at end of file diff --git a/mcs/class/Mono.C5/C5/hashing/HashTable.cs b/mcs/class/Mono.C5/C5/hashing/HashTable.cs index d38579693f3..7dbab93a0b7 100644 --- a/mcs/class/Mono.C5/C5/hashing/HashTable.cs +++ b/mcs/class/Mono.C5/C5/hashing/HashTable.cs @@ -1,1640 +1,1640 @@ -/*
- Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-
-#define LINEARPROBINGnot
-#define REFBUCKET
-#define SHRINKnot
-#define INTERHASHINGnot
-#define RANDOMINTERHASHING
-
-using System;
-using System.Diagnostics;
-using SCG = System.Collections.Generic;
-
-namespace C5
-{
- /// <summary>
- /// A set collection class based on linear hashing
- /// </summary>
- [Serializable]
- public class HashSet<T> : CollectionBase<T>, ICollection<T>
- {
- #region Feature
- /// <summary>
- /// Enum class to assist printing of compilation alternatives.
- /// </summary>
- [Flags]
- public enum Feature : short
- {
- /// <summary>
- /// Nothing
- /// </summary>
- Dummy = 0,
- /// <summary>
- /// Buckets are of reference type
- /// </summary>
- RefTypeBucket = 1,
- /// <summary>
- /// Primary buckets are of value type
- /// </summary>
- ValueTypeBucket = 2,
- /// <summary>
- /// Using linear probing to resolve index clashes
- /// </summary>
- LinearProbing = 4,
- /// <summary>
- /// Shrink table when very sparsely filled
- /// </summary>
- ShrinkTable = 8,
- /// <summary>
- /// Use chaining to resolve index clashes
- /// </summary>
- Chaining = 16,
- /// <summary>
- /// Use hash function on item hash code
- /// </summary>
- InterHashing = 32,
- /// <summary>
- /// Use a universal family of hash functions on item hash code
- /// </summary>
- RandomInterHashing = 64
- }
-
-
-
- static Feature features = Feature.Dummy
-#if REFBUCKET
- | Feature.RefTypeBucket
-#else
- | Feature.ValueTypeBucket
-#endif
-#if SHRINK
- | Feature.ShrinkTable
-#endif
-#if LINEARPROBING
- | Feature.LinearProbing
-#else
- | Feature.Chaining
-#endif
-#if INTERHASHING
- | Feature.InterHashing
-#elif RANDOMINTERHASHING
- | Feature.RandomInterHashing
-#endif
-;
-
-
- /// <summary>
- /// Show which implementation features was chosen at compilation time
- /// </summary>
- public static Feature Features { get { return features; } }
-
- #endregion
-
- #region Fields
-
- int indexmask, bits, bitsc, origbits, lastchosen; //bitsc==32-bits; indexmask==(1<<bits)-1;
-
- Bucket[] table;
-
-#if !REFBUCKET
- bool defaultvalid = false;
-
- T defaultitem;
-#endif
- double fillfactor = 0.66;
-
- int resizethreshhold;
-
-#if RANDOMINTERHASHING
-#if DEBUG
- const uint randomhashfactor = 1529784659;
-#else
- uint randomhashfactor = (2 * (uint)(new Random()).Next() + 1) * 1529784659;
-#endif
-#endif
-
- #endregion
-
- #region Events
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public override EventTypeEnum ListenableEvents { get { return EventTypeEnum.Basic; } }
-
- #endregion
-
- #region Bucket nested class(es)
-#if REFBUCKET
- [Serializable]
- class Bucket
- {
- internal T item;
-
- internal int hashval; //Cache!
-
-#if LINEARPROBING
- internal Bucket(T item, int hashval)
- {
- this.item = item;
- this.hashval = hashval;
- }
-#else
- internal Bucket overflow;
-
- internal Bucket(T item, int hashval, Bucket overflow)
- {
- this.item = item;
- this.hashval = hashval;
- this.overflow = overflow;
- }
-#endif
- }
-#else
- struct Bucket
- {
- internal T item;
-
- internal int hashval; //Cache!
-
-#if LINEARPROBING
- internal Bucket(T item, int hashval)
- {
- this.item = item;
- this.hashval = hashval;
- }
-#else
- internal OverflowBucket overflow;
-
-
- internal Bucket(T item, int hashval)
- {
- this.item = item;
- this.hashval = hashval;
- this.overflow = default(OverflowBucket);
- }
-#endif
- }
-
-
-#if !LINEARPROBING
- class OverflowBucket
- {
- internal T item;
-
- internal int hashval; //Cache!
-
- internal OverflowBucket overflow;
-
-
- internal OverflowBucket(T item, int hashval, OverflowBucket overflow)
- {
- this.item = item;
- this.hashval = hashval;
- this.overflow = overflow;
- }
- }
-#endif
-#endif
-
- #endregion
-
- #region Basic Util
-
- bool equals(T i1, T i2) { return itemequalityComparer.Equals(i1, i2); }
-
-#if !REFBUCKET
- bool isnull(T item) { return itemequalityComparer.Equals(item, default(T)); }
-#endif
-
- int gethashcode(T item) { return itemequalityComparer.GetHashCode(item); }
-
-
- int hv2i(int hashval)
- {
-#if INTERHASHING
- //Note: *inverse mod 2^32 is -1503427877
- return (int)(((uint)hashval * 1529784659) >>bitsc);
-#elif RANDOMINTERHASHING
- return (int)(((uint)hashval * randomhashfactor) >> bitsc);
-#else
- return indexmask & hashval;
-#endif
- }
-
-
- void expand()
- {
- //Console.WriteLine(String.Format("Expand to {0} bits", bits+1));
- resize(bits + 1);
- }
-
-
- void shrink()
- {
- if (bits > 3)
- {
- //Console.WriteLine(String.Format("Shrink to {0} bits", bits - 1));
- resize(bits - 1);
- }
- }
-
-
- void resize(int bits)
- {
- //Console.WriteLine(String.Format("Resize to {0} bits", bits));
- this.bits = bits;
- bitsc = 32 - bits;
- indexmask = (1 << bits) - 1;
-
- Bucket[] newtable = new Bucket[indexmask + 1];
-
- for (int i = 0, s = table.Length; i < s; i++)
- {
- Bucket b = table[i];
-
-#if LINEARPROBING
-#if REFBUCKET
- if (b != null)
- {
- int j = hv2i(b.hashval);
-
- while (newtable[j] != null) { j = indexmask & (j + 1); }
-
- newtable[j] = b;
- }
-#else
- if (!isnull(b.item))
- {
- int j = hv2i(b.hashval);
-
- while (!isnull(newtable[j].item)) { j = indexmask & (j + 1); }
-
- newtable[j] = b;
- }
-#endif
-#else
-#if REFBUCKET
- while (b != null)
- {
- int j = hv2i(b.hashval);
-
- newtable[j] = new Bucket(b.item, b.hashval, newtable[j]);
- b = b.overflow;
- }
-#else
- if (!isnull(b.item))
- {
- insert(b.item, b.hashval, newtable);
-
- OverflowBucket ob = b.overflow;
-
- while (ob != null)
- {
- insert(ob.item, ob.hashval, newtable);
- ob = ob.overflow;
- }
- }
-#endif
-#endif
- }
-
- table = newtable;
- resizethreshhold = (int)(table.Length * fillfactor);
- //Console.WriteLine(String.Format("Resize to {0} bits done", bits));
- }
-
-#if REFBUCKET
-#else
-#if LINEARPROBING
-#else
- //Only for resize!!!
- private void insert(T item, int hashval, Bucket[] t)
- {
- int i = hv2i(hashval);
- Bucket b = t[i];
-
- if (!isnull(b.item))
- {
- t[i].overflow = new OverflowBucket(item, hashval, b.overflow);
- }
- else
- t[i] = new Bucket(item, hashval);
- }
-#endif
-#endif
-
- /// <summary>
- /// Search for an item equal (according to itemequalityComparer) to the supplied item.
- /// </summary>
- /// <param name="item"></param>
- /// <param name="add">If true, add item to table if not found.</param>
- /// <param name="update">If true, update table entry if item found.</param>
- /// <param name="raise">If true raise events</param>
- /// <returns>True if found</returns>
- private bool searchoradd(ref T item, bool add, bool update, bool raise)
- {
-
-#if LINEARPROBING
-#if REFBUCKET
- int hashval = gethashcode(item);
- int i = hv2i(hashval);
- Bucket b = table[i];
-
- while (b != null)
- {
- T olditem = b.item;
- if (equals(olditem, item))
- {
- if (update)
- b.item = item;
- else
- item = olditem;
-
- if (raise && update)
- raiseForUpdate(item, olditem);
- return true;
- }
-
- b = table[i = indexmask & (i + 1)];
- }
-
- if (!add) goto notfound;
-
- table[i] = new Bucket(item, hashval);
-
-#else
- if (isnull(item))
- {
- if (defaultvalid)
- {
- T olditem = defaultitem;
- if (update)
- defaultitem = item;
- else
- item = defaultitem;
-
- if (raise && update)
- raiseForUpdate(item, olditem);
- return true;
- }
-
- if (!add) goto notfound;
-
- defaultvalid = true;
- defaultitem = item;
- }
- else
- {
- int hashval = gethashcode(item);
- int i = hv2i(hashval);
- T t = table[i].item;
-
- while (!isnull(t))
- {
- if (equals(t, item))
- {
- if (update)
- table[i].item = item;
- else
- item = t;
-
- if (raise && update)
- raiseForUpdate(item, t);
- return true;
- }
-
- t = table[i = indexmask & (i + 1)].item;
- }
-
- if (!add) goto notfound;
-
- table[i] = new Bucket(item, hashval);
- }
-#endif
-#else
-#if REFBUCKET
- int hashval = gethashcode(item);
- int i = hv2i(hashval);
- Bucket b = table[i], bold = null;
-
- if (b != null)
- {
- while (b != null)
- {
- T olditem = b.item;
- if (equals(olditem, item))
- {
- if (update)
- {
- b.item = item;
- }
- if (raise && update)
- raiseForUpdate(item, olditem);
- // bug20071112:
- item = olditem;
- return true;
- }
-
- bold = b;
- b = b.overflow;
- }
-
- if (!add) goto notfound;
-
- bold.overflow = new Bucket(item, hashval, null);
- }
- else
- {
- if (!add) goto notfound;
-
- table[i] = new Bucket(item, hashval, null);
- }
-#else
- if (isnull(item))
- {
- if (defaultvalid)
- {
- T olditem = defaultitem;
- if (update)
- defaultitem = item;
- else
- item = defaultitem;
-
- if (raise && update)
- raiseForUpdate(item, olditem);
- return true;
- }
-
- if (!add) goto notfound;
-
- defaultvalid = true;
- defaultitem = item;
- }
- else
- {
- int hashval = gethashcode(item);
- int i = hv2i(hashval);
- Bucket b = table[i];
-
- if (!isnull(b.item))
- {
- if (equals(b.item, item))
- {
- if (update)
- table[i].item = item;
- else
- item = b.item;
-
- if (raise && update)
- raiseForUpdate(item, b.item);
- return true;
- }
-
- OverflowBucket ob = table[i].overflow;
-
- if (ob == null)
- {
- if (!add) goto notfound;
-
- table[i].overflow = new OverflowBucket(item, hashval, null);
- }
- else
- {
- T olditem = ob.item;
- while (ob.overflow != null)
- {
- if (equals(item, olditem))
- {
- if (update)
- ob.item = item;
- else
- item = olditem;
-
- if (raise && update)
- raiseForUpdate(item, olditem);
- return true;
- }
-
- ob = ob.overflow;
- olditem = ob.item;
- }
-
- if (equals(item, olditem))
- {
- if (update)
- ob.item = item;
- else
- item = olditem;
-
- if (raise && update)
- raiseForUpdate(item, olditem);
- return true;
- }
-
- if (!add) goto notfound;
-
- ob.overflow = new OverflowBucket(item, hashval, null);
- }
- }
- else
- {
- if (!add) goto notfound;
-
- table[i] = new Bucket(item, hashval);
- }
- }
-#endif
-#endif
- size++;
- if (size > resizethreshhold)
- expand();
- notfound:
- if (raise && add)
- raiseForAdd(item);
- if (update)
- item = default(T);
- return false;
- }
-
-
- private bool remove(ref T item)
- {
-
- if (size == 0)
- return false;
-#if LINEARPROBING
-#if REFBUCKET
- int hashval = gethashcode(item);
- int index = hv2i(hashval);
- Bucket b = table[index];
-
- while (b != null)
- {
- if (equals(item, b.item))
- {
- //ref
- item = table[index].item;
- table[index] = null;
-
- //Algorithm R
- int j = (index + 1) & indexmask;
-
- b = table[j];
- while (b != null)
- {
- int k = hv2i(b.hashval);
-
- if ((k <= index && index < j) || (index < j && j < k) || (j < k && k <= index))
- //if (index > j ? (j < k && k <= index): (k <= index || j < k) )
- {
- table[index] = b;
- table[j] = null;
- index = j;
- }
-
- j = (j + 1) & indexmask;
- b = table[j];
- }
-
- goto found;
- }
-
- b = table[index = indexmask & (index + 1)];
- }
- return false;
-#else
- if (isnull(item))
- {
- if (!defaultvalid)
- return false;
-
- //ref
- item = defaultitem;
- defaultvalid = false;
- defaultitem = default(T); //No spaceleaks!
- }
- else
- {
- int hashval = gethashcode(item);
- int index = hv2i(hashval);
- T t = table[index].item;
-
- while (!isnull(t))
- {
- if (equals(item, t))
- {
- //ref
- item = table[index].item;
- table[index].item = default(T);
-
- //algorithm R
- int j = (index + 1) & indexmask;
- Bucket b = table[j];
-
- while (!isnull(b.item))
- {
- int k = hv2i(b.hashval);
-
- if ((k <= index && index < j) || (index < j && j < k) || (j < k && k <= index))
- {
- table[index] = b;
- table[j].item = default(T);
- index = j;
- }
-
- j = (j + 1) & indexmask;
- b = table[j];
- }
-
- goto found;
- }
-
- t = table[index = indexmask & (index + 1)].item;
- }
-
- return false;
- }
-#endif
- found:
-#else
-#if REFBUCKET
- int hashval = gethashcode(item);
- int index = hv2i(hashval);
- Bucket b = table[index], bold;
-
- if (b == null)
- return false;
-
- if (equals(item, b.item))
- {
- //ref
- item = b.item;
- table[index] = b.overflow;
- }
- else
- {
- bold = b;
- b = b.overflow;
- while (b != null && !equals(item, b.item))
- {
- bold = b;
- b = b.overflow;
- }
-
- if (b == null)
- return false;
-
- //ref
- item = b.item;
- bold.overflow = b.overflow;
- }
-
-#else
- if (isnull(item))
- {
- if (!defaultvalid)
- return false;
-
- //ref
- item = defaultitem;
- defaultvalid = false;
- defaultitem = default(T); //No spaceleaks!
- }
- else
- {
- int hashval = gethashcode(item);
- int index = hv2i(hashval);
- Bucket b = table[index];
- OverflowBucket ob = b.overflow;
-
- if (equals(item, b.item))
- {
- //ref
- item = b.item;
- if (ob == null)
- {
- table[index] = new Bucket();
- }
- else
- {
- b = new Bucket(ob.item, ob.hashval);
- b.overflow = ob.overflow;
- table[index] = b;
- }
- }
- else
- {
- if (ob == null)
- return false;
-
- if (equals(item, ob.item))
- {
- //ref
- item=ob.item;
- table[index].overflow = ob.overflow;
- }
- else
- {
- while (ob.overflow != null)
- if (equals(item, ob.overflow.item))
- {
- //ref
- item = ob.overflow.item;
- break;
- }
- else
- ob = ob.overflow;
-
- if (ob.overflow == null)
- return false;
-
- ob.overflow = ob.overflow.overflow;
- }
- }
- }
-#endif
-#endif
- size--;
-
- return true;
- }
-
-
- private void clear()
- {
- bits = origbits;
- bitsc = 32 - bits;
- indexmask = (1 << bits) - 1;
- size = 0;
- table = new Bucket[indexmask + 1];
- resizethreshhold = (int)(table.Length * fillfactor);
-#if !REFBUCKET
- defaultitem = default(T);
- defaultvalid = false;
-#endif
- }
-
- #endregion
-
- #region Constructors
- /// <summary>
- /// Create a hash set with natural item equalityComparer and default fill threshold (66%)
- /// and initial table size (16).
- /// </summary>
- public HashSet()
- : this(EqualityComparer<T>.Default) { }
-
-
- /// <summary>
- /// Create a hash set with external item equalityComparer and default fill threshold (66%)
- /// and initial table size (16).
- /// </summary>
- /// <param name="itemequalityComparer">The external item equalityComparer</param>
- public HashSet(SCG.IEqualityComparer<T> itemequalityComparer)
- : this(16, itemequalityComparer) { }
-
-
- /// <summary>
- /// Create a hash set with external item equalityComparer and default fill threshold (66%)
- /// </summary>
- /// <param name="capacity">Initial table size (rounded to power of 2, at least 16)</param>
- /// <param name="itemequalityComparer">The external item equalityComparer</param>
- public HashSet(int capacity, SCG.IEqualityComparer<T> itemequalityComparer)
- : this(capacity, 0.66, itemequalityComparer) { }
-
-
- /// <summary>
- /// Create a hash set with external item equalityComparer.
- /// </summary>
- /// <param name="capacity">Initial table size (rounded to power of 2, at least 16)</param>
- /// <param name="fill">Fill threshold (in range 10% to 90%)</param>
- /// <param name="itemequalityComparer">The external item equalityComparer</param>
- public HashSet(int capacity, double fill, SCG.IEqualityComparer<T> itemequalityComparer)
- : base(itemequalityComparer)
- {
- if (fill < 0.1 || fill > 0.9)
- throw new ArgumentException("Fill outside valid range [0.1, 0.9]");
- if (capacity <= 0)
- throw new ArgumentException("Capacity must be non-negative");
- //this.itemequalityComparer = itemequalityComparer;
- origbits = 4;
- while (capacity - 1 >> origbits > 0) origbits++;
- clear();
- }
-
-
-
- #endregion
-
- #region IEditableCollection<T> Members
-
- /// <summary>
- /// The complexity of the Contains operation
- /// </summary>
- /// <value>Always returns Speed.Constant</value>
- [Tested]
- public virtual Speed ContainsSpeed { [Tested]get { return Speed.Constant; } }
-
- /// <summary>
- /// Check if an item is in the set
- /// </summary>
- /// <param name="item">The item to look for</param>
- /// <returns>True if set contains item</returns>
- [Tested]
- public virtual bool Contains(T item) { return searchoradd(ref item, false, false, false); }
-
-
- /// <summary>
- /// Check if an item (collection equal to a given one) is in the set and
- /// if so report the actual item object found.
- /// </summary>
- /// <param name="item">On entry, the item to look for.
- /// On exit the item found, if any</param>
- /// <returns>True if set contains item</returns>
- [Tested]
- public virtual bool Find(ref T item) { return searchoradd(ref item, false, false, false); }
-
-
- /// <summary>
- /// Check if an item (collection equal to a given one) is in the set and
- /// if so replace the item object in the set with the supplied one.
- /// </summary>
- /// <param name="item">The item object to update with</param>
- /// <returns>True if item was found (and updated)</returns>
- [Tested]
- public virtual bool Update(T item)
- { updatecheck(); return searchoradd(ref item, false, true, true); }
-
- /// <summary>
- /// Check if an item (collection equal to a given one) is in the set and
- /// if so replace the item object in the set with the supplied one.
- /// </summary>
- /// <param name="item">The item object to update with</param>
- /// <param name="olditem"></param>
- /// <returns>True if item was found (and updated)</returns>
- public virtual bool Update(T item, out T olditem)
- { updatecheck(); olditem = item; return searchoradd(ref olditem, false, true, true); }
-
-
- /// <summary>
- /// Check if an item (collection equal to a given one) is in the set.
- /// If found, report the actual item object in the set,
- /// else add the supplied one.
- /// </summary>
- /// <param name="item">On entry, the item to look for or add.
- /// On exit the actual object found, if any.</param>
- /// <returns>True if item was found</returns>
- [Tested]
- public virtual bool FindOrAdd(ref T item)
- { updatecheck(); return searchoradd(ref item, true, false, true); }
-
-
- /// <summary>
- /// Check if an item (collection equal to a supplied one) is in the set and
- /// if so replace the item object in the set with the supplied one; else
- /// add the supplied one.
- /// </summary>
- /// <param name="item">The item to look for and update or add</param>
- /// <returns>True if item was updated</returns>
- [Tested]
- public virtual bool UpdateOrAdd(T item)
- { updatecheck(); return searchoradd(ref item, true, true, true); }
-
-
- /// <summary>
- /// Check if an item (collection equal to a supplied one) is in the set and
- /// if so replace the item object in the set with the supplied one; else
- /// add the supplied one.
- /// </summary>
- /// <param name="item">The item to look for and update or add</param>
- /// <param name="olditem"></param>
- /// <returns>True if item was updated</returns>
- public virtual bool UpdateOrAdd(T item, out T olditem)
- { updatecheck(); olditem = item; return searchoradd(ref olditem, true, true, true); }
-
-
- /// <summary>
- /// Remove an item from the set
- /// </summary>
- /// <param name="item">The item to remove</param>
- /// <returns>True if item was (found and) removed </returns>
- [Tested]
- public virtual bool Remove(T item)
- {
- updatecheck();
- if (remove(ref item))
- {
-#if SHRINK
- if (size<resizethreshhold/2 && resizethreshhold>8)
- shrink();
-#endif
- raiseForRemove(item);
- return true;
- }
- else
- return false;
- }
-
-
- /// <summary>
- /// Remove an item from the set, reporting the actual matching item object.
- /// </summary>
- /// <param name="item">The value to remove.</param>
- /// <param name="removeditem">The removed value.</param>
- /// <returns>True if item was found.</returns>
- [Tested]
- public virtual bool Remove(T item, out T removeditem)
- {
- updatecheck();
- removeditem = item;
- if (remove(ref removeditem))
- {
-#if SHRINK
- if (size<resizethreshhold/2 && resizethreshhold>8)
- shrink();
-#endif
- raiseForRemove(removeditem);
- return true;
- }
- else
- return false;
- }
-
-
- /// <summary>
- /// Remove all items in a supplied collection from this set.
- /// </summary>
- /// <typeparam name="U"></typeparam>
- /// <param name="items">The items to remove.</param>
- [Tested]
- public virtual void RemoveAll<U>(SCG.IEnumerable<U> items) where U : T
- {
- updatecheck();
- RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(this);
- bool raise = raiseHandler.MustFire;
- T jtem;
- foreach (U item in items)
- { jtem = item; if (remove(ref jtem) && raise) raiseHandler.Remove(jtem); }
-#if SHRINK
- if (size < resizethreshhold / 2 && resizethreshhold > 16)
- {
- int newlength = table.Length;
-
- while (newlength >= 32 && newlength * fillfactor / 2 > size)
- newlength /= 2;
-
- resize(newlength - 1);
- }
-#endif
- if (raise) raiseHandler.Raise();
- }
-
- /// <summary>
- /// Remove all items from the set, resetting internal table to initial size.
- /// </summary>
- [Tested]
- public virtual void Clear()
- {
- updatecheck();
- int oldsize = size;
- clear();
- if (ActiveEvents != 0 && oldsize > 0)
- {
- raiseCollectionCleared(true, oldsize);
- raiseCollectionChanged();
- }
- }
-
-
- /// <summary>
- /// Remove all items *not* in a supplied collection from this set.
- /// </summary>
- /// <typeparam name="U"></typeparam>
- /// <param name="items">The items to retain</param>
- [Tested]
- public virtual void RetainAll<U>(SCG.IEnumerable<U> items) where U : T
- {
- updatecheck();
-
- HashSet<T> aux = new HashSet<T>(EqualityComparer);
-
- //This only works for sets:
- foreach (U item in items)
- if (Contains(item))
- {
- T jtem = item;
-
- aux.searchoradd(ref jtem, true, false, false);
- }
-
- if (size == aux.size)
- return;
-
- CircularQueue<T> wasRemoved = null;
- if ((ActiveEvents & EventTypeEnum.Removed) != 0)
- {
- wasRemoved = new CircularQueue<T>();
- foreach (T item in this)
- if (!aux.Contains(item))
- wasRemoved.Enqueue(item);
- }
-
- table = aux.table;
- size = aux.size;
-#if !REFBUCKET
- defaultvalid = aux.defaultvalid;
- defaultitem = aux.defaultitem;
-#endif
- indexmask = aux.indexmask;
- resizethreshhold = aux.resizethreshhold;
- bits = aux.bits;
- bitsc = aux.bitsc;
-#if DEBUG
-#else
- randomhashfactor = aux.randomhashfactor;
-#endif
-
- if ((ActiveEvents & EventTypeEnum.Removed) != 0)
- raiseForRemoveAll(wasRemoved);
- else if ((ActiveEvents & EventTypeEnum.Changed) != 0)
- raiseCollectionChanged();
- }
-
- /// <summary>
- /// Check if all items in a supplied collection is in this set
- /// (ignoring multiplicities).
- /// </summary>
- /// <param name="items">The items to look for.</param>
- /// <typeparam name="U"></typeparam>
- /// <returns>True if all items are found.</returns>
- [Tested]
- public virtual bool ContainsAll<U>(SCG.IEnumerable<U> items) where U : T
- {
- foreach (U item in items)
- if (!Contains(item))
- return false;
- return true;
- }
-
-
- /// <summary>
- /// Create an array containing all items in this set (in enumeration order).
- /// </summary>
- /// <returns>The array</returns>
- [Tested]
- public override T[] ToArray()
- {
- T[] res = new T[size];
- int index = 0;
-
-#if !REFBUCKET
- if (defaultvalid)
- res[index++] = defaultitem;
-#endif
- for (int i = 0; i < table.Length; i++)
- {
- Bucket b = table[i];
-#if LINEARPROBING
-#if REFBUCKET
- if (b != null)
- res[index++] = b.item;
-#else
- if (!isnull(b.item))
- res[index++] = b.item;
-#endif
-#else
-#if REFBUCKET
- while (b != null)
- {
- res[index++] = b.item;
- b = b.overflow;
- }
-#else
- if (!isnull(b.item))
- {
- res[index++] = b.item;
-
- OverflowBucket ob = b.overflow;
-
- while (ob != null)
- {
- res[index++] = ob.item;
- ob = ob.overflow;
- }
- }
-#endif
-#endif
- }
-
- Debug.Assert(size == index);
- return res;
- }
-
-
- /// <summary>
- /// Count the number of times an item is in this set (either 0 or 1).
- /// </summary>
- /// <param name="item">The item to look for.</param>
- /// <returns>1 if item is in set, 0 else</returns>
- [Tested]
- public virtual int ContainsCount(T item) { return Contains(item) ? 1 : 0; }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public virtual ICollectionValue<T> UniqueItems() { return this; }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public virtual ICollectionValue<KeyValuePair<T, int>> ItemMultiplicities()
- {
- return new MultiplicityOne<T>(this);
- }
-
- /// <summary>
- /// Remove all (at most 1) copies of item from this set.
- /// </summary>
- /// <param name="item">The item to remove</param>
- [Tested]
- public virtual void RemoveAllCopies(T item) { Remove(item); }
-
- #endregion
-
- #region IEnumerable<T> Members
-
-
- /// <summary>
- /// Choose some item of this collection.
- /// </summary>
- /// <exception cref="NoSuchItemException">if collection is empty.</exception>
- /// <returns></returns>
- [Tested]
- public override T Choose()
- {
- int len = table.Length;
- if (size == 0)
- throw new NoSuchItemException();
-#if REFBUCKET
- do { if (++lastchosen >= len) lastchosen = 0; } while (table[lastchosen] == null);
-#else
- if (defaultvalid) return defaultitem;
- do { if (++lastchosen >= len) lastchosen = 0; } while (isnull(table[lastchosen].item));
-#endif
- return table[lastchosen].item;
- }
-
- /// <summary>
- /// Create an enumerator for this set.
- /// </summary>
- /// <returns>The enumerator</returns>
- [Tested]
- public override SCG.IEnumerator<T> GetEnumerator()
- {
- int index = -1;
- int mystamp = stamp;
- int len = table.Length;
-
-#if LINEARPROBING
-#if REFBUCKET
- while (++index < len)
- {
- if (mystamp != stamp) throw new CollectionModifiedException();
-
- if (table[index] != null) yield return table[index].item;
- }
-#else
- if (defaultvalid)
- yield return defaultitem;
-
- while (++index < len)
- {
- if (mystamp != stamp) throw new CollectionModifiedException();
-
- T item = table[index].item;
-
- if (!isnull(item)) yield return item;
- }
-#endif
-#else
-#if REFBUCKET
- Bucket b = null;
-#else
- OverflowBucket ob = null;
-
- if (defaultvalid)
- yield return defaultitem;
-#endif
- while (true)
- {
- if (mystamp != stamp)
- throw new CollectionModifiedException();
-
-#if REFBUCKET
- if (b == null || b.overflow == null)
- {
- do
- {
- if (++index >= len) yield break;
- } while (table[index] == null);
-
- b = table[index];
- yield return b.item;
- }
- else
- {
- b = b.overflow;
- yield return b.item;
- }
-#else
- if (ob != null && ob.overflow != null)
- {
- ob = ob.overflow;
- yield return ob.item;
- }
- else if (index >= 0 && ob == null && (ob = table[index].overflow) != null)
- {
- yield return ob.item;
- }
- else
- {
- do
- {
- if (++index >= len) yield break;
- } while (isnull(table[index].item));
-
- yield return table[index].item;
- ob = null;
- }
-#endif
- }
-#endif
- }
-
- #endregion
-
- #region ISink<T> Members
- /// <summary>
- /// Report if this is a set collection.
- /// </summary>
- /// <value>Always false</value>
- [Tested]
- public virtual bool AllowsDuplicates { [Tested]get { return false; } }
-
- /// <summary>
- /// By convention this is true for any collection with set semantics.
- /// </summary>
- /// <value>True if only one representative of a group of equal items
- /// is kept in the collection together with the total count.</value>
- public virtual bool DuplicatesByCounting { get { return true; } }
-
- /// <summary>
- /// Add an item to this set.
- /// </summary>
- /// <param name="item">The item to add.</param>
- /// <returns>True if item was added (i.e. not found)</returns>
- [Tested]
- public virtual bool Add(T item)
- {
- updatecheck();
- return !searchoradd(ref item, true, false, true);
- }
-
- /// <summary>
- /// Add an item to this set.
- /// </summary>
- /// <param name="item">The item to add.</param>
- [Tested]
- void SCG.ICollection<T>.Add(T item)
- {
- Add(item);
- }
-
- /// <summary>
- /// Add the elements from another collection with a more specialized item type
- /// to this collection. Since this
- /// collection has set semantics, only items not already in the collection
- /// will be added.
- /// </summary>
- /// <typeparam name="U">The type of items to add</typeparam>
- /// <param name="items">The items to add</param>
- [Tested]
- public virtual void AddAll<U>(SCG.IEnumerable<U> items) where U : T
- {
- updatecheck();
- bool wasChanged = false;
- bool raiseAdded = (ActiveEvents & EventTypeEnum.Added) != 0;
- CircularQueue<T> wasAdded = raiseAdded ? new CircularQueue<T>() : null;
- foreach (T item in items)
- {
- T jtem = item;
-
- if (!searchoradd(ref jtem, true, false, false))
- {
- wasChanged = true;
- if (raiseAdded)
- wasAdded.Enqueue(item);
- }
- }
- //TODO: implement a RaiseForAddAll() method
- if (raiseAdded & wasChanged)
- foreach (T item in wasAdded)
- raiseItemsAdded(item, 1);
- if (((ActiveEvents & EventTypeEnum.Changed) != 0 && wasChanged))
- raiseCollectionChanged();
- }
-
-
- #endregion
-
- #region Diagnostics
-
- /// <summary>
- /// Test internal structure of data (invariants)
- /// </summary>
- /// <returns>True if pass</returns>
- [Tested]
- public virtual bool Check()
- {
- int count = 0;
-#if LINEARPROBING
- int lasthole = table.Length - 1;
-
-#if REFBUCKET
- while (lasthole >= 0 && table[lasthole] != null)
-#else
- while (lasthole >= 0 && !isnull(table[lasthole].item))
-#endif
- {
- lasthole--;
- count++;
- }
-
- if (lasthole < 0)
- {
- Console.WriteLine("Table is completely filled!");
- return false;
- }
-
- for (int cellindex = lasthole + 1, s = table.Length; cellindex < s; cellindex++)
- {
- Bucket b = table[cellindex];
- int hashindex = hv2i(b.hashval);
-
- if (hashindex <= lasthole || hashindex > cellindex)
- {
- Console.WriteLine("Bad cell item={0}, hashval={1}, hashindex={2}, cellindex={3}, lasthole={4}", b.item, b.hashval, hashindex, cellindex, lasthole);
- return false;
- }
- }
-
- int latesthole = -1;
-
- for (int cellindex = 0; cellindex < lasthole; cellindex++)
- {
- Bucket b = table[cellindex];
-
-#if REFBUCKET
- if (b != null)
-#else
- if (!isnull(b.item))
-#endif
- {
- count++;
-
- int hashindex = hv2i(b.hashval);
-
- if (cellindex < hashindex && hashindex <= lasthole)
- {
- Console.WriteLine("Bad cell item={0}, hashval={1}, hashindex={2}, cellindex={3}, latesthole={4}", b.item, b.hashval, hashindex, cellindex, latesthole);
- return false;
- }
- }
- else
- {
- latesthole = cellindex;
- break;
- }
- }
-
- for (int cellindex = latesthole + 1; cellindex < lasthole; cellindex++)
- {
- Bucket b = table[cellindex];
-
-#if REFBUCKET
- if (b != null)
-#else
- if (!isnull(b.item))
-#endif
- {
- count++;
-
- int hashindex = hv2i(b.hashval);
-
- if (hashindex <= latesthole || cellindex < hashindex)
- {
- Console.WriteLine("Bad cell item={0}, hashval={1}, hashindex={2}, cellindex={3}, latesthole={4}", b.item, b.hashval, hashindex, cellindex, latesthole);
- return false;
- }
- }
- else
- {
- latesthole = cellindex;
- }
- }
-
- return true;
-#else
- bool retval = true;
-
- if (bitsc != 32 - bits)
- {
- Console.WriteLine("bitsc != 32 - bits ({0}, {1})", bitsc, bits);
- retval = false;
- }
- if (indexmask != (1 << bits) - 1)
- {
- Console.WriteLine("indexmask != (1 << bits) - 1 ({0}, {1})", indexmask, bits);
- retval = false;
- }
- if (table.Length != indexmask + 1)
- {
- Console.WriteLine("table.Length != indexmask + 1 ({0}, {1})", table.Length, indexmask);
- retval = false;
- }
- if (bitsc != 32 - bits)
- {
- Console.WriteLine("resizethreshhold != (int)(table.Length * fillfactor) ({0}, {1}, {2})", resizethreshhold, table.Length, fillfactor);
- retval = false;
- }
-
- for (int i = 0, s = table.Length; i < s; i++)
- {
- int level = 0;
- Bucket b = table[i];
-#if REFBUCKET
- while (b != null)
- {
- if (i != hv2i(b.hashval))
- {
- Console.WriteLine("Bad cell item={0}, hashval={1}, index={2}, level={3}", b.item, b.hashval, i, level);
- retval = false;
- }
-
- count++;
- level++;
- b = b.overflow;
- }
-#else
- if (!isnull(b.item))
- {
- count++;
- if (i != hv2i(b.hashval))
- {
- Console.WriteLine("Bad cell item={0}, hashval={1}, index={2}, level={3}", b.item, b.hashval, i, level);
- retval = false;
- }
-
- OverflowBucket ob = b.overflow;
-
- while (ob != null)
- {
- level++;
- count++;
- if (i != hv2i(ob.hashval))
- {
- Console.WriteLine("Bad cell item={0}, hashval={1}, index={2}, level={3}", b.item, b.hashval, i, level);
- retval = false;
- }
-
- ob = ob.overflow;
- }
- }
-#endif
- }
-
- if (count != size)
- {
- Console.WriteLine("size({0}) != count({1})", size, count);
- retval = false;
- }
-
- return retval;
-#endif
- }
-
-
- /// <summary>
- /// Produce statistics on distribution of bucket sizes. Current implementation is incomplete.
- /// </summary>
- /// <returns>Histogram data.</returns>
- [Tested(via = "Manually")]
- public ISortedDictionary<int, int> BucketCostDistribution()
- {
- TreeDictionary<int, int> res = new TreeDictionary<int, int>();
-#if LINEARPROBING
- int count = 0;
-#if REFBUCKET
- while (table[count] != null)
-#else
- while (!isnull(table[count].item))
-#endif
- count++;
- for (int i = table.Length - 1; i >= 0; i--)
- {
-#if REFBUCKET
- if (table[i] != null)
-#else
- if (!isnull(table[i].item))
-#endif
- count++;
- else
- count = 0;
- if (res.Contains(count))
- res[count]++;
- else
- res[count] = 1;
- }
-
- return res;
-#else
- for (int i = 0, s = table.Length; i < s; i++)
- {
- int count = 0;
-#if REFBUCKET
- Bucket b = table[i];
-
- while (b != null)
- {
- count++;
- b = b.overflow;
- }
-#else
- Bucket b = table[i];
-
- if (!isnull(b.item))
- {
- count = 1;
-
- OverflowBucket ob = b.overflow;
-
- while (ob != null)
- {
- count++;
- ob = ob.overflow;
- }
- }
-#endif
- if (res.Contains(count))
- res[count]++;
- else
- res[count] = 1;
- }
-
- return res;
-#endif
- }
-
- #endregion
-
- #region ICloneable Members
-
- /// <summary>
- /// Make a shallow copy of this HashSet.
- /// </summary>
- /// <returns></returns>
- public virtual object Clone()
- {
- HashSet<T> clone = new HashSet<T>(size > 0 ? size : 1, itemequalityComparer);
- //TODO: make sure this really adds in the counting bag way!
- clone.AddAll(this);
- return clone;
- }
-
- #endregion
-
- }
-}
+/* + Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#define LINEARPROBINGnot +#define REFBUCKET +#define SHRINKnot +#define INTERHASHINGnot +#define RANDOMINTERHASHING + +using System; +using System.Diagnostics; +using SCG = System.Collections.Generic; + +namespace C5 +{ + /// <summary> + /// A set collection class based on linear hashing + /// </summary> + [Serializable] + public class HashSet<T> : CollectionBase<T>, ICollection<T> + { + #region Feature + /// <summary> + /// Enum class to assist printing of compilation alternatives. + /// </summary> + [Flags] + public enum Feature : short + { + /// <summary> + /// Nothing + /// </summary> + Dummy = 0, + /// <summary> + /// Buckets are of reference type + /// </summary> + RefTypeBucket = 1, + /// <summary> + /// Primary buckets are of value type + /// </summary> + ValueTypeBucket = 2, + /// <summary> + /// Using linear probing to resolve index clashes + /// </summary> + LinearProbing = 4, + /// <summary> + /// Shrink table when very sparsely filled + /// </summary> + ShrinkTable = 8, + /// <summary> + /// Use chaining to resolve index clashes + /// </summary> + Chaining = 16, + /// <summary> + /// Use hash function on item hash code + /// </summary> + InterHashing = 32, + /// <summary> + /// Use a universal family of hash functions on item hash code + /// </summary> + RandomInterHashing = 64 + } + + + + static Feature features = Feature.Dummy +#if REFBUCKET + | Feature.RefTypeBucket +#else + | Feature.ValueTypeBucket +#endif +#if SHRINK + | Feature.ShrinkTable +#endif +#if LINEARPROBING + | Feature.LinearProbing +#else + | Feature.Chaining +#endif +#if INTERHASHING + | Feature.InterHashing +#elif RANDOMINTERHASHING + | Feature.RandomInterHashing +#endif +; + + + /// <summary> + /// Show which implementation features was chosen at compilation time + /// </summary> + public static Feature Features { get { return features; } } + + #endregion + + #region Fields + + int indexmask, bits, bitsc, origbits, lastchosen; //bitsc==32-bits; indexmask==(1<<bits)-1; + + Bucket[] table; + +#if !REFBUCKET + bool defaultvalid = false; + + T defaultitem; +#endif + double fillfactor = 0.66; + + int resizethreshhold; + +#if RANDOMINTERHASHING +#if DEBUG + const uint randomhashfactor = 1529784659; +#else + uint randomhashfactor = (2 * (uint)(new Random()).Next() + 1) * 1529784659; +#endif +#endif + + #endregion + + #region Events + + /// <summary> + /// + /// </summary> + /// <value></value> + public override EventTypeEnum ListenableEvents { get { return EventTypeEnum.Basic; } } + + #endregion + + #region Bucket nested class(es) +#if REFBUCKET + [Serializable] + class Bucket + { + internal T item; + + internal int hashval; //Cache! + +#if LINEARPROBING + internal Bucket(T item, int hashval) + { + this.item = item; + this.hashval = hashval; + } +#else + internal Bucket overflow; + + internal Bucket(T item, int hashval, Bucket overflow) + { + this.item = item; + this.hashval = hashval; + this.overflow = overflow; + } +#endif + } +#else + struct Bucket + { + internal T item; + + internal int hashval; //Cache! + +#if LINEARPROBING + internal Bucket(T item, int hashval) + { + this.item = item; + this.hashval = hashval; + } +#else + internal OverflowBucket overflow; + + + internal Bucket(T item, int hashval) + { + this.item = item; + this.hashval = hashval; + this.overflow = default(OverflowBucket); + } +#endif + } + + +#if !LINEARPROBING + class OverflowBucket + { + internal T item; + + internal int hashval; //Cache! + + internal OverflowBucket overflow; + + + internal OverflowBucket(T item, int hashval, OverflowBucket overflow) + { + this.item = item; + this.hashval = hashval; + this.overflow = overflow; + } + } +#endif +#endif + + #endregion + + #region Basic Util + + bool equals(T i1, T i2) { return itemequalityComparer.Equals(i1, i2); } + +#if !REFBUCKET + bool isnull(T item) { return itemequalityComparer.Equals(item, default(T)); } +#endif + + int gethashcode(T item) { return itemequalityComparer.GetHashCode(item); } + + + int hv2i(int hashval) + { +#if INTERHASHING + //Note: *inverse mod 2^32 is -1503427877 + return (int)(((uint)hashval * 1529784659) >>bitsc); +#elif RANDOMINTERHASHING + return (int)(((uint)hashval * randomhashfactor) >> bitsc); +#else + return indexmask & hashval; +#endif + } + + + void expand() + { + //Console.WriteLine(String.Format("Expand to {0} bits", bits+1)); + resize(bits + 1); + } + + + void shrink() + { + if (bits > 3) + { + //Console.WriteLine(String.Format("Shrink to {0} bits", bits - 1)); + resize(bits - 1); + } + } + + + void resize(int bits) + { + //Console.WriteLine(String.Format("Resize to {0} bits", bits)); + this.bits = bits; + bitsc = 32 - bits; + indexmask = (1 << bits) - 1; + + Bucket[] newtable = new Bucket[indexmask + 1]; + + for (int i = 0, s = table.Length; i < s; i++) + { + Bucket b = table[i]; + +#if LINEARPROBING +#if REFBUCKET + if (b != null) + { + int j = hv2i(b.hashval); + + while (newtable[j] != null) { j = indexmask & (j + 1); } + + newtable[j] = b; + } +#else + if (!isnull(b.item)) + { + int j = hv2i(b.hashval); + + while (!isnull(newtable[j].item)) { j = indexmask & (j + 1); } + + newtable[j] = b; + } +#endif +#else +#if REFBUCKET + while (b != null) + { + int j = hv2i(b.hashval); + + newtable[j] = new Bucket(b.item, b.hashval, newtable[j]); + b = b.overflow; + } +#else + if (!isnull(b.item)) + { + insert(b.item, b.hashval, newtable); + + OverflowBucket ob = b.overflow; + + while (ob != null) + { + insert(ob.item, ob.hashval, newtable); + ob = ob.overflow; + } + } +#endif +#endif + } + + table = newtable; + resizethreshhold = (int)(table.Length * fillfactor); + //Console.WriteLine(String.Format("Resize to {0} bits done", bits)); + } + +#if REFBUCKET +#else +#if LINEARPROBING +#else + //Only for resize!!! + private void insert(T item, int hashval, Bucket[] t) + { + int i = hv2i(hashval); + Bucket b = t[i]; + + if (!isnull(b.item)) + { + t[i].overflow = new OverflowBucket(item, hashval, b.overflow); + } + else + t[i] = new Bucket(item, hashval); + } +#endif +#endif + + /// <summary> + /// Search for an item equal (according to itemequalityComparer) to the supplied item. + /// </summary> + /// <param name="item"></param> + /// <param name="add">If true, add item to table if not found.</param> + /// <param name="update">If true, update table entry if item found.</param> + /// <param name="raise">If true raise events</param> + /// <returns>True if found</returns> + private bool searchoradd(ref T item, bool add, bool update, bool raise) + { + +#if LINEARPROBING +#if REFBUCKET + int hashval = gethashcode(item); + int i = hv2i(hashval); + Bucket b = table[i]; + + while (b != null) + { + T olditem = b.item; + if (equals(olditem, item)) + { + if (update) + b.item = item; + else + item = olditem; + + if (raise && update) + raiseForUpdate(item, olditem); + return true; + } + + b = table[i = indexmask & (i + 1)]; + } + + if (!add) goto notfound; + + table[i] = new Bucket(item, hashval); + +#else + if (isnull(item)) + { + if (defaultvalid) + { + T olditem = defaultitem; + if (update) + defaultitem = item; + else + item = defaultitem; + + if (raise && update) + raiseForUpdate(item, olditem); + return true; + } + + if (!add) goto notfound; + + defaultvalid = true; + defaultitem = item; + } + else + { + int hashval = gethashcode(item); + int i = hv2i(hashval); + T t = table[i].item; + + while (!isnull(t)) + { + if (equals(t, item)) + { + if (update) + table[i].item = item; + else + item = t; + + if (raise && update) + raiseForUpdate(item, t); + return true; + } + + t = table[i = indexmask & (i + 1)].item; + } + + if (!add) goto notfound; + + table[i] = new Bucket(item, hashval); + } +#endif +#else +#if REFBUCKET + int hashval = gethashcode(item); + int i = hv2i(hashval); + Bucket b = table[i], bold = null; + + if (b != null) + { + while (b != null) + { + T olditem = b.item; + if (equals(olditem, item)) + { + if (update) + { + b.item = item; + } + if (raise && update) + raiseForUpdate(item, olditem); + // bug20071112: + item = olditem; + return true; + } + + bold = b; + b = b.overflow; + } + + if (!add) goto notfound; + + bold.overflow = new Bucket(item, hashval, null); + } + else + { + if (!add) goto notfound; + + table[i] = new Bucket(item, hashval, null); + } +#else + if (isnull(item)) + { + if (defaultvalid) + { + T olditem = defaultitem; + if (update) + defaultitem = item; + else + item = defaultitem; + + if (raise && update) + raiseForUpdate(item, olditem); + return true; + } + + if (!add) goto notfound; + + defaultvalid = true; + defaultitem = item; + } + else + { + int hashval = gethashcode(item); + int i = hv2i(hashval); + Bucket b = table[i]; + + if (!isnull(b.item)) + { + if (equals(b.item, item)) + { + if (update) + table[i].item = item; + else + item = b.item; + + if (raise && update) + raiseForUpdate(item, b.item); + return true; + } + + OverflowBucket ob = table[i].overflow; + + if (ob == null) + { + if (!add) goto notfound; + + table[i].overflow = new OverflowBucket(item, hashval, null); + } + else + { + T olditem = ob.item; + while (ob.overflow != null) + { + if (equals(item, olditem)) + { + if (update) + ob.item = item; + else + item = olditem; + + if (raise && update) + raiseForUpdate(item, olditem); + return true; + } + + ob = ob.overflow; + olditem = ob.item; + } + + if (equals(item, olditem)) + { + if (update) + ob.item = item; + else + item = olditem; + + if (raise && update) + raiseForUpdate(item, olditem); + return true; + } + + if (!add) goto notfound; + + ob.overflow = new OverflowBucket(item, hashval, null); + } + } + else + { + if (!add) goto notfound; + + table[i] = new Bucket(item, hashval); + } + } +#endif +#endif + size++; + if (size > resizethreshhold) + expand(); + notfound: + if (raise && add) + raiseForAdd(item); + if (update) + item = default(T); + return false; + } + + + private bool remove(ref T item) + { + + if (size == 0) + return false; +#if LINEARPROBING +#if REFBUCKET + int hashval = gethashcode(item); + int index = hv2i(hashval); + Bucket b = table[index]; + + while (b != null) + { + if (equals(item, b.item)) + { + //ref + item = table[index].item; + table[index] = null; + + //Algorithm R + int j = (index + 1) & indexmask; + + b = table[j]; + while (b != null) + { + int k = hv2i(b.hashval); + + if ((k <= index && index < j) || (index < j && j < k) || (j < k && k <= index)) + //if (index > j ? (j < k && k <= index): (k <= index || j < k) ) + { + table[index] = b; + table[j] = null; + index = j; + } + + j = (j + 1) & indexmask; + b = table[j]; + } + + goto found; + } + + b = table[index = indexmask & (index + 1)]; + } + return false; +#else + if (isnull(item)) + { + if (!defaultvalid) + return false; + + //ref + item = defaultitem; + defaultvalid = false; + defaultitem = default(T); //No spaceleaks! + } + else + { + int hashval = gethashcode(item); + int index = hv2i(hashval); + T t = table[index].item; + + while (!isnull(t)) + { + if (equals(item, t)) + { + //ref + item = table[index].item; + table[index].item = default(T); + + //algorithm R + int j = (index + 1) & indexmask; + Bucket b = table[j]; + + while (!isnull(b.item)) + { + int k = hv2i(b.hashval); + + if ((k <= index && index < j) || (index < j && j < k) || (j < k && k <= index)) + { + table[index] = b; + table[j].item = default(T); + index = j; + } + + j = (j + 1) & indexmask; + b = table[j]; + } + + goto found; + } + + t = table[index = indexmask & (index + 1)].item; + } + + return false; + } +#endif + found: +#else +#if REFBUCKET + int hashval = gethashcode(item); + int index = hv2i(hashval); + Bucket b = table[index], bold; + + if (b == null) + return false; + + if (equals(item, b.item)) + { + //ref + item = b.item; + table[index] = b.overflow; + } + else + { + bold = b; + b = b.overflow; + while (b != null && !equals(item, b.item)) + { + bold = b; + b = b.overflow; + } + + if (b == null) + return false; + + //ref + item = b.item; + bold.overflow = b.overflow; + } + +#else + if (isnull(item)) + { + if (!defaultvalid) + return false; + + //ref + item = defaultitem; + defaultvalid = false; + defaultitem = default(T); //No spaceleaks! + } + else + { + int hashval = gethashcode(item); + int index = hv2i(hashval); + Bucket b = table[index]; + OverflowBucket ob = b.overflow; + + if (equals(item, b.item)) + { + //ref + item = b.item; + if (ob == null) + { + table[index] = new Bucket(); + } + else + { + b = new Bucket(ob.item, ob.hashval); + b.overflow = ob.overflow; + table[index] = b; + } + } + else + { + if (ob == null) + return false; + + if (equals(item, ob.item)) + { + //ref + item=ob.item; + table[index].overflow = ob.overflow; + } + else + { + while (ob.overflow != null) + if (equals(item, ob.overflow.item)) + { + //ref + item = ob.overflow.item; + break; + } + else + ob = ob.overflow; + + if (ob.overflow == null) + return false; + + ob.overflow = ob.overflow.overflow; + } + } + } +#endif +#endif + size--; + + return true; + } + + + private void clear() + { + bits = origbits; + bitsc = 32 - bits; + indexmask = (1 << bits) - 1; + size = 0; + table = new Bucket[indexmask + 1]; + resizethreshhold = (int)(table.Length * fillfactor); +#if !REFBUCKET + defaultitem = default(T); + defaultvalid = false; +#endif + } + + #endregion + + #region Constructors + /// <summary> + /// Create a hash set with natural item equalityComparer and default fill threshold (66%) + /// and initial table size (16). + /// </summary> + public HashSet() + : this(EqualityComparer<T>.Default) { } + + + /// <summary> + /// Create a hash set with external item equalityComparer and default fill threshold (66%) + /// and initial table size (16). + /// </summary> + /// <param name="itemequalityComparer">The external item equalityComparer</param> + public HashSet(SCG.IEqualityComparer<T> itemequalityComparer) + : this(16, itemequalityComparer) { } + + + /// <summary> + /// Create a hash set with external item equalityComparer and default fill threshold (66%) + /// </summary> + /// <param name="capacity">Initial table size (rounded to power of 2, at least 16)</param> + /// <param name="itemequalityComparer">The external item equalityComparer</param> + public HashSet(int capacity, SCG.IEqualityComparer<T> itemequalityComparer) + : this(capacity, 0.66, itemequalityComparer) { } + + + /// <summary> + /// Create a hash set with external item equalityComparer. + /// </summary> + /// <param name="capacity">Initial table size (rounded to power of 2, at least 16)</param> + /// <param name="fill">Fill threshold (in range 10% to 90%)</param> + /// <param name="itemequalityComparer">The external item equalityComparer</param> + public HashSet(int capacity, double fill, SCG.IEqualityComparer<T> itemequalityComparer) + : base(itemequalityComparer) + { + if (fill < 0.1 || fill > 0.9) + throw new ArgumentException("Fill outside valid range [0.1, 0.9]"); + if (capacity <= 0) + throw new ArgumentException("Capacity must be non-negative"); + //this.itemequalityComparer = itemequalityComparer; + origbits = 4; + while (capacity - 1 >> origbits > 0) origbits++; + clear(); + } + + + + #endregion + + #region IEditableCollection<T> Members + + /// <summary> + /// The complexity of the Contains operation + /// </summary> + /// <value>Always returns Speed.Constant</value> + [Tested] + public virtual Speed ContainsSpeed { [Tested]get { return Speed.Constant; } } + + /// <summary> + /// Check if an item is in the set + /// </summary> + /// <param name="item">The item to look for</param> + /// <returns>True if set contains item</returns> + [Tested] + public virtual bool Contains(T item) { return searchoradd(ref item, false, false, false); } + + + /// <summary> + /// Check if an item (collection equal to a given one) is in the set and + /// if so report the actual item object found. + /// </summary> + /// <param name="item">On entry, the item to look for. + /// On exit the item found, if any</param> + /// <returns>True if set contains item</returns> + [Tested] + public virtual bool Find(ref T item) { return searchoradd(ref item, false, false, false); } + + + /// <summary> + /// Check if an item (collection equal to a given one) is in the set and + /// if so replace the item object in the set with the supplied one. + /// </summary> + /// <param name="item">The item object to update with</param> + /// <returns>True if item was found (and updated)</returns> + [Tested] + public virtual bool Update(T item) + { updatecheck(); return searchoradd(ref item, false, true, true); } + + /// <summary> + /// Check if an item (collection equal to a given one) is in the set and + /// if so replace the item object in the set with the supplied one. + /// </summary> + /// <param name="item">The item object to update with</param> + /// <param name="olditem"></param> + /// <returns>True if item was found (and updated)</returns> + public virtual bool Update(T item, out T olditem) + { updatecheck(); olditem = item; return searchoradd(ref olditem, false, true, true); } + + + /// <summary> + /// Check if an item (collection equal to a given one) is in the set. + /// If found, report the actual item object in the set, + /// else add the supplied one. + /// </summary> + /// <param name="item">On entry, the item to look for or add. + /// On exit the actual object found, if any.</param> + /// <returns>True if item was found</returns> + [Tested] + public virtual bool FindOrAdd(ref T item) + { updatecheck(); return searchoradd(ref item, true, false, true); } + + + /// <summary> + /// Check if an item (collection equal to a supplied one) is in the set and + /// if so replace the item object in the set with the supplied one; else + /// add the supplied one. + /// </summary> + /// <param name="item">The item to look for and update or add</param> + /// <returns>True if item was updated</returns> + [Tested] + public virtual bool UpdateOrAdd(T item) + { updatecheck(); return searchoradd(ref item, true, true, true); } + + + /// <summary> + /// Check if an item (collection equal to a supplied one) is in the set and + /// if so replace the item object in the set with the supplied one; else + /// add the supplied one. + /// </summary> + /// <param name="item">The item to look for and update or add</param> + /// <param name="olditem"></param> + /// <returns>True if item was updated</returns> + public virtual bool UpdateOrAdd(T item, out T olditem) + { updatecheck(); olditem = item; return searchoradd(ref olditem, true, true, true); } + + + /// <summary> + /// Remove an item from the set + /// </summary> + /// <param name="item">The item to remove</param> + /// <returns>True if item was (found and) removed </returns> + [Tested] + public virtual bool Remove(T item) + { + updatecheck(); + if (remove(ref item)) + { +#if SHRINK + if (size<resizethreshhold/2 && resizethreshhold>8) + shrink(); +#endif + raiseForRemove(item); + return true; + } + else + return false; + } + + + /// <summary> + /// Remove an item from the set, reporting the actual matching item object. + /// </summary> + /// <param name="item">The value to remove.</param> + /// <param name="removeditem">The removed value.</param> + /// <returns>True if item was found.</returns> + [Tested] + public virtual bool Remove(T item, out T removeditem) + { + updatecheck(); + removeditem = item; + if (remove(ref removeditem)) + { +#if SHRINK + if (size<resizethreshhold/2 && resizethreshhold>8) + shrink(); +#endif + raiseForRemove(removeditem); + return true; + } + else + return false; + } + + + /// <summary> + /// Remove all items in a supplied collection from this set. + /// </summary> + /// <typeparam name="U"></typeparam> + /// <param name="items">The items to remove.</param> + [Tested] + public virtual void RemoveAll<U>(SCG.IEnumerable<U> items) where U : T + { + updatecheck(); + RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(this); + bool raise = raiseHandler.MustFire; + T jtem; + foreach (U item in items) + { jtem = item; if (remove(ref jtem) && raise) raiseHandler.Remove(jtem); } +#if SHRINK + if (size < resizethreshhold / 2 && resizethreshhold > 16) + { + int newlength = table.Length; + + while (newlength >= 32 && newlength * fillfactor / 2 > size) + newlength /= 2; + + resize(newlength - 1); + } +#endif + if (raise) raiseHandler.Raise(); + } + + /// <summary> + /// Remove all items from the set, resetting internal table to initial size. + /// </summary> + [Tested] + public virtual void Clear() + { + updatecheck(); + int oldsize = size; + clear(); + if (ActiveEvents != 0 && oldsize > 0) + { + raiseCollectionCleared(true, oldsize); + raiseCollectionChanged(); + } + } + + + /// <summary> + /// Remove all items *not* in a supplied collection from this set. + /// </summary> + /// <typeparam name="U"></typeparam> + /// <param name="items">The items to retain</param> + [Tested] + public virtual void RetainAll<U>(SCG.IEnumerable<U> items) where U : T + { + updatecheck(); + + HashSet<T> aux = new HashSet<T>(EqualityComparer); + + //This only works for sets: + foreach (U item in items) + if (Contains(item)) + { + T jtem = item; + + aux.searchoradd(ref jtem, true, false, false); + } + + if (size == aux.size) + return; + + CircularQueue<T> wasRemoved = null; + if ((ActiveEvents & EventTypeEnum.Removed) != 0) + { + wasRemoved = new CircularQueue<T>(); + foreach (T item in this) + if (!aux.Contains(item)) + wasRemoved.Enqueue(item); + } + + table = aux.table; + size = aux.size; +#if !REFBUCKET + defaultvalid = aux.defaultvalid; + defaultitem = aux.defaultitem; +#endif + indexmask = aux.indexmask; + resizethreshhold = aux.resizethreshhold; + bits = aux.bits; + bitsc = aux.bitsc; +#if DEBUG +#else + randomhashfactor = aux.randomhashfactor; +#endif + + if ((ActiveEvents & EventTypeEnum.Removed) != 0) + raiseForRemoveAll(wasRemoved); + else if ((ActiveEvents & EventTypeEnum.Changed) != 0) + raiseCollectionChanged(); + } + + /// <summary> + /// Check if all items in a supplied collection is in this set + /// (ignoring multiplicities). + /// </summary> + /// <param name="items">The items to look for.</param> + /// <typeparam name="U"></typeparam> + /// <returns>True if all items are found.</returns> + [Tested] + public virtual bool ContainsAll<U>(SCG.IEnumerable<U> items) where U : T + { + foreach (U item in items) + if (!Contains(item)) + return false; + return true; + } + + + /// <summary> + /// Create an array containing all items in this set (in enumeration order). + /// </summary> + /// <returns>The array</returns> + [Tested] + public override T[] ToArray() + { + T[] res = new T[size]; + int index = 0; + +#if !REFBUCKET + if (defaultvalid) + res[index++] = defaultitem; +#endif + for (int i = 0; i < table.Length; i++) + { + Bucket b = table[i]; +#if LINEARPROBING +#if REFBUCKET + if (b != null) + res[index++] = b.item; +#else + if (!isnull(b.item)) + res[index++] = b.item; +#endif +#else +#if REFBUCKET + while (b != null) + { + res[index++] = b.item; + b = b.overflow; + } +#else + if (!isnull(b.item)) + { + res[index++] = b.item; + + OverflowBucket ob = b.overflow; + + while (ob != null) + { + res[index++] = ob.item; + ob = ob.overflow; + } + } +#endif +#endif + } + + Debug.Assert(size == index); + return res; + } + + + /// <summary> + /// Count the number of times an item is in this set (either 0 or 1). + /// </summary> + /// <param name="item">The item to look for.</param> + /// <returns>1 if item is in set, 0 else</returns> + [Tested] + public virtual int ContainsCount(T item) { return Contains(item) ? 1 : 0; } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public virtual ICollectionValue<T> UniqueItems() { return this; } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public virtual ICollectionValue<KeyValuePair<T, int>> ItemMultiplicities() + { + return new MultiplicityOne<T>(this); + } + + /// <summary> + /// Remove all (at most 1) copies of item from this set. + /// </summary> + /// <param name="item">The item to remove</param> + [Tested] + public virtual void RemoveAllCopies(T item) { Remove(item); } + + #endregion + + #region IEnumerable<T> Members + + + /// <summary> + /// Choose some item of this collection. + /// </summary> + /// <exception cref="NoSuchItemException">if collection is empty.</exception> + /// <returns></returns> + [Tested] + public override T Choose() + { + int len = table.Length; + if (size == 0) + throw new NoSuchItemException(); +#if REFBUCKET + do { if (++lastchosen >= len) lastchosen = 0; } while (table[lastchosen] == null); +#else + if (defaultvalid) return defaultitem; + do { if (++lastchosen >= len) lastchosen = 0; } while (isnull(table[lastchosen].item)); +#endif + return table[lastchosen].item; + } + + /// <summary> + /// Create an enumerator for this set. + /// </summary> + /// <returns>The enumerator</returns> + [Tested] + public override SCG.IEnumerator<T> GetEnumerator() + { + int index = -1; + int mystamp = stamp; + int len = table.Length; + +#if LINEARPROBING +#if REFBUCKET + while (++index < len) + { + if (mystamp != stamp) throw new CollectionModifiedException(); + + if (table[index] != null) yield return table[index].item; + } +#else + if (defaultvalid) + yield return defaultitem; + + while (++index < len) + { + if (mystamp != stamp) throw new CollectionModifiedException(); + + T item = table[index].item; + + if (!isnull(item)) yield return item; + } +#endif +#else +#if REFBUCKET + Bucket b = null; +#else + OverflowBucket ob = null; + + if (defaultvalid) + yield return defaultitem; +#endif + while (true) + { + if (mystamp != stamp) + throw new CollectionModifiedException(); + +#if REFBUCKET + if (b == null || b.overflow == null) + { + do + { + if (++index >= len) yield break; + } while (table[index] == null); + + b = table[index]; + yield return b.item; + } + else + { + b = b.overflow; + yield return b.item; + } +#else + if (ob != null && ob.overflow != null) + { + ob = ob.overflow; + yield return ob.item; + } + else if (index >= 0 && ob == null && (ob = table[index].overflow) != null) + { + yield return ob.item; + } + else + { + do + { + if (++index >= len) yield break; + } while (isnull(table[index].item)); + + yield return table[index].item; + ob = null; + } +#endif + } +#endif + } + + #endregion + + #region ISink<T> Members + /// <summary> + /// Report if this is a set collection. + /// </summary> + /// <value>Always false</value> + [Tested] + public virtual bool AllowsDuplicates { [Tested]get { return false; } } + + /// <summary> + /// By convention this is true for any collection with set semantics. + /// </summary> + /// <value>True if only one representative of a group of equal items + /// is kept in the collection together with the total count.</value> + public virtual bool DuplicatesByCounting { get { return true; } } + + /// <summary> + /// Add an item to this set. + /// </summary> + /// <param name="item">The item to add.</param> + /// <returns>True if item was added (i.e. not found)</returns> + [Tested] + public virtual bool Add(T item) + { + updatecheck(); + return !searchoradd(ref item, true, false, true); + } + + /// <summary> + /// Add an item to this set. + /// </summary> + /// <param name="item">The item to add.</param> + [Tested] + void SCG.ICollection<T>.Add(T item) + { + Add(item); + } + + /// <summary> + /// Add the elements from another collection with a more specialized item type + /// to this collection. Since this + /// collection has set semantics, only items not already in the collection + /// will be added. + /// </summary> + /// <typeparam name="U">The type of items to add</typeparam> + /// <param name="items">The items to add</param> + [Tested] + public virtual void AddAll<U>(SCG.IEnumerable<U> items) where U : T + { + updatecheck(); + bool wasChanged = false; + bool raiseAdded = (ActiveEvents & EventTypeEnum.Added) != 0; + CircularQueue<T> wasAdded = raiseAdded ? new CircularQueue<T>() : null; + foreach (T item in items) + { + T jtem = item; + + if (!searchoradd(ref jtem, true, false, false)) + { + wasChanged = true; + if (raiseAdded) + wasAdded.Enqueue(item); + } + } + //TODO: implement a RaiseForAddAll() method + if (raiseAdded & wasChanged) + foreach (T item in wasAdded) + raiseItemsAdded(item, 1); + if (((ActiveEvents & EventTypeEnum.Changed) != 0 && wasChanged)) + raiseCollectionChanged(); + } + + + #endregion + + #region Diagnostics + + /// <summary> + /// Test internal structure of data (invariants) + /// </summary> + /// <returns>True if pass</returns> + [Tested] + public virtual bool Check() + { + int count = 0; +#if LINEARPROBING + int lasthole = table.Length - 1; + +#if REFBUCKET + while (lasthole >= 0 && table[lasthole] != null) +#else + while (lasthole >= 0 && !isnull(table[lasthole].item)) +#endif + { + lasthole--; + count++; + } + + if (lasthole < 0) + { + Console.WriteLine("Table is completely filled!"); + return false; + } + + for (int cellindex = lasthole + 1, s = table.Length; cellindex < s; cellindex++) + { + Bucket b = table[cellindex]; + int hashindex = hv2i(b.hashval); + + if (hashindex <= lasthole || hashindex > cellindex) + { + Console.WriteLine("Bad cell item={0}, hashval={1}, hashindex={2}, cellindex={3}, lasthole={4}", b.item, b.hashval, hashindex, cellindex, lasthole); + return false; + } + } + + int latesthole = -1; + + for (int cellindex = 0; cellindex < lasthole; cellindex++) + { + Bucket b = table[cellindex]; + +#if REFBUCKET + if (b != null) +#else + if (!isnull(b.item)) +#endif + { + count++; + + int hashindex = hv2i(b.hashval); + + if (cellindex < hashindex && hashindex <= lasthole) + { + Console.WriteLine("Bad cell item={0}, hashval={1}, hashindex={2}, cellindex={3}, latesthole={4}", b.item, b.hashval, hashindex, cellindex, latesthole); + return false; + } + } + else + { + latesthole = cellindex; + break; + } + } + + for (int cellindex = latesthole + 1; cellindex < lasthole; cellindex++) + { + Bucket b = table[cellindex]; + +#if REFBUCKET + if (b != null) +#else + if (!isnull(b.item)) +#endif + { + count++; + + int hashindex = hv2i(b.hashval); + + if (hashindex <= latesthole || cellindex < hashindex) + { + Console.WriteLine("Bad cell item={0}, hashval={1}, hashindex={2}, cellindex={3}, latesthole={4}", b.item, b.hashval, hashindex, cellindex, latesthole); + return false; + } + } + else + { + latesthole = cellindex; + } + } + + return true; +#else + bool retval = true; + + if (bitsc != 32 - bits) + { + Console.WriteLine("bitsc != 32 - bits ({0}, {1})", bitsc, bits); + retval = false; + } + if (indexmask != (1 << bits) - 1) + { + Console.WriteLine("indexmask != (1 << bits) - 1 ({0}, {1})", indexmask, bits); + retval = false; + } + if (table.Length != indexmask + 1) + { + Console.WriteLine("table.Length != indexmask + 1 ({0}, {1})", table.Length, indexmask); + retval = false; + } + if (bitsc != 32 - bits) + { + Console.WriteLine("resizethreshhold != (int)(table.Length * fillfactor) ({0}, {1}, {2})", resizethreshhold, table.Length, fillfactor); + retval = false; + } + + for (int i = 0, s = table.Length; i < s; i++) + { + int level = 0; + Bucket b = table[i]; +#if REFBUCKET + while (b != null) + { + if (i != hv2i(b.hashval)) + { + Console.WriteLine("Bad cell item={0}, hashval={1}, index={2}, level={3}", b.item, b.hashval, i, level); + retval = false; + } + + count++; + level++; + b = b.overflow; + } +#else + if (!isnull(b.item)) + { + count++; + if (i != hv2i(b.hashval)) + { + Console.WriteLine("Bad cell item={0}, hashval={1}, index={2}, level={3}", b.item, b.hashval, i, level); + retval = false; + } + + OverflowBucket ob = b.overflow; + + while (ob != null) + { + level++; + count++; + if (i != hv2i(ob.hashval)) + { + Console.WriteLine("Bad cell item={0}, hashval={1}, index={2}, level={3}", b.item, b.hashval, i, level); + retval = false; + } + + ob = ob.overflow; + } + } +#endif + } + + if (count != size) + { + Console.WriteLine("size({0}) != count({1})", size, count); + retval = false; + } + + return retval; +#endif + } + + + /// <summary> + /// Produce statistics on distribution of bucket sizes. Current implementation is incomplete. + /// </summary> + /// <returns>Histogram data.</returns> + [Tested(via = "Manually")] + public ISortedDictionary<int, int> BucketCostDistribution() + { + TreeDictionary<int, int> res = new TreeDictionary<int, int>(); +#if LINEARPROBING + int count = 0; +#if REFBUCKET + while (table[count] != null) +#else + while (!isnull(table[count].item)) +#endif + count++; + for (int i = table.Length - 1; i >= 0; i--) + { +#if REFBUCKET + if (table[i] != null) +#else + if (!isnull(table[i].item)) +#endif + count++; + else + count = 0; + if (res.Contains(count)) + res[count]++; + else + res[count] = 1; + } + + return res; +#else + for (int i = 0, s = table.Length; i < s; i++) + { + int count = 0; +#if REFBUCKET + Bucket b = table[i]; + + while (b != null) + { + count++; + b = b.overflow; + } +#else + Bucket b = table[i]; + + if (!isnull(b.item)) + { + count = 1; + + OverflowBucket ob = b.overflow; + + while (ob != null) + { + count++; + ob = ob.overflow; + } + } +#endif + if (res.Contains(count)) + res[count]++; + else + res[count] = 1; + } + + return res; +#endif + } + + #endregion + + #region ICloneable Members + + /// <summary> + /// Make a shallow copy of this HashSet. + /// </summary> + /// <returns></returns> + public virtual object Clone() + { + HashSet<T> clone = new HashSet<T>(size > 0 ? size : 1, itemequalityComparer); + //TODO: make sure this really adds in the counting bag way! + clone.AddAll(this); + return clone; + } + + #endregion + + } +} diff --git a/mcs/class/Mono.C5/C5/linkedlists/HashedLinkedList.cs b/mcs/class/Mono.C5/C5/linkedlists/HashedLinkedList.cs index 566fdfb7491..bd028c4197d 100644 --- a/mcs/class/Mono.C5/C5/linkedlists/HashedLinkedList.cs +++ b/mcs/class/Mono.C5/C5/linkedlists/HashedLinkedList.cs @@ -1,3919 +1,3919 @@ -/*
- Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-
-#define HASHINDEX
-
-using System;
-using System.Diagnostics;
-using SCG = System.Collections.Generic;
-
-namespace C5
-{
- /// <summary>
- /// A list collection class based on a doubly linked list data structure.
- /// </summary>
- [Serializable]
- public class HashedLinkedList<T> : SequencedBase<T>, IList<T>, SCG.IList<T>
-#if HASHINDEX
-#else
-, IStack<T>, IQueue<T>
-#endif
- {
- #region Fields
- /// <summary>
- /// IExtensible.Add(T) always does AddLast(T), fIFO determines
- /// if T Remove() does RemoveFirst() or RemoveLast()
- /// </summary>
- bool fIFO = true;
-
- #region Events
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public override EventTypeEnum ListenableEvents { get { return underlying == null ? EventTypeEnum.All : EventTypeEnum.None; } }
-
- #endregion
-
- //Invariant: startsentinel != null && endsentinel != null
- //If size==0: startsentinel.next == endsentinel && endsentinel.prev == startsentinel
- //Else: startsentinel.next == First && endsentinel.prev == Last)
- /// <summary>
- /// Node to the left of first node
- /// </summary>
- Node startsentinel;
- /// <summary>
- /// Node to the right of last node
- /// </summary>
- Node endsentinel;
- /// <summary>
- /// Offset of this view in underlying list
- /// </summary>
-#if HASHINDEX
- int? offset;
-#else
- int offset;
-#endif
-
- /// <summary>
- /// underlying list of this view (or null for the underlying list)
- /// </summary>
- HashedLinkedList<T> underlying;
-
- //Note: all views will have the same views list since all view objects are created by MemberwiseClone()
- WeakViewList<HashedLinkedList<T>> views;
- WeakViewList<HashedLinkedList<T>>.Node myWeakReference;
-
- /// <summary>
- /// Has this list or view not been invalidated by some operation (by someone calling Dispose())
- /// </summary>
- bool isValid = true;
-
-
-#if HASHINDEX
- HashDictionary<T, Node> dict;
- /// <summary>
- /// Number of taggroups
- /// </summary>
- int taggroups;
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- int Taggroups
- {
- get { return underlying == null ? taggroups : underlying.taggroups; }
- set { if (underlying == null) taggroups = value; else underlying.taggroups = value; }
- }
-#endif
-
- #endregion
-
- #region Util
-
- bool equals(T i1, T i2) { return itemequalityComparer.Equals(i1, i2); }
-
- #region Check utilities
- /// <summary>
- /// Check if it is valid to perform updates and increment stamp of
- /// underlying if this is a view.
- /// <para>This method should be called in every public modifying
- /// methods before any modifications are performed.
- /// </para>
- /// </summary>
- /// <exception cref="InvalidOperationException"> if check fails.</exception>
- protected override void updatecheck()
- {
- validitycheck();
- base.updatecheck();
- if (underlying != null)
- underlying.stamp++;
- }
-
- /// <summary>
- /// Check if we are a view that the underlyinglist has only been updated through us.
- /// <br/>
- /// This method should be called from enumerators etc to guard against
- /// modification of the base collection.
- /// </summary>
- /// <exception cref="InvalidOperationException"> if check fails.</exception>
- void validitycheck()
- {
- if (!isValid)
- throw new ViewDisposedException();
- }
-
- /// <summary>
- /// Check that the list has not been updated since a particular time.
- /// </summary>
- /// <param name="stamp">The stamp indicating the time.</param>
- /// <exception cref="CollectionModifiedException"> if check fails.</exception>
- protected override void modifycheck(int stamp)
- {
- validitycheck();
- if ((underlying != null ? underlying.stamp : this.stamp) != stamp)
- throw new CollectionModifiedException();
- }
- #endregion
-
- #region Searching
- bool contains(T item, out Node node)
- {
-#if HASHINDEX
- if (dict.Find(item, out node))
- return insideview(node);
-#else
- //TODO: search from both ends? Or search from the end selected by FIFO?
- node = startsentinel.next;
- while (node != endsentinel)
- {
- if (equals(item, node.item))
- return true;
- node = node.next;
- }
-#endif
- return false;
- }
-
- /// <summary>
- /// Search forwards from a node for a node with a particular item.
- /// </summary>
- /// <param name="item">The item to look for</param>
- /// <param name="node">On input, the node to start at. If item was found, the node found on output.</param>
- /// <param name="index">If node was found, the value will be the number of links followed higher than
- /// the value on input. If item was not found, the value on output is undefined.</param>
- /// <returns>True if node was found.</returns>
- bool find(T item, ref Node node, ref int index)
- {
- while (node != endsentinel)
- {
- //if (item.Equals(node.item))
- if (itemequalityComparer.Equals(item, node.item))
- return true;
-
- index++;
- node = node.next;
- }
-
- return false;
- }
-
- bool dnif(T item, ref Node node, ref int index)
- {
- while (node != startsentinel)
- {
- //if (item.Equals(node.item))
- if (itemequalityComparer.Equals(item, node.item))
- return true;
-
- index--;
- node = node.prev;
- }
-
- return false;
- }
-
-#if HASHINDEX
- bool insideview(Node node)
- {
- if (underlying == null)
- return true;
- return (startsentinel.precedes(node) && node.precedes(endsentinel));
- }
-#endif
-
- #endregion
-
- #region Indexing
- /// <summary>
- /// Return the node at position pos
- /// </summary>
- /// <param name="pos"></param>
- /// <returns></returns>
- Node get(int pos)
- {
- if (pos < 0 || pos >= size)
- throw new IndexOutOfRangeException();
- else if (pos < size / 2)
- { // Closer to front
- Node node = startsentinel;
-
- for (int i = 0; i <= pos; i++)
- node = node.next;
-
- return node;
- }
- else
- { // Closer to end
- Node node = endsentinel;
-
- for (int i = size; i > pos; i--)
- node = node.prev;
-
- return node;
- }
- }
-
- /// <summary>
- /// Find the distance from pos to the set given by positions. Return the
- /// signed distance as return value and as an out parameter, the
- /// array index of the nearest position. This is used for up to length 5 of
- /// positions, and we do not assume it is sorted.
- /// </summary>
- /// <param name="pos"></param>
- /// <param name="positions"></param>
- /// <param name="nearest"></param>
- /// <returns></returns>
- int dist(int pos, out int nearest, int[] positions)
- {
- nearest = -1;
- int bestdist = int.MaxValue;
- int signeddist = bestdist;
- for (int i = 0; i < positions.Length; i++)
- {
- int thisdist = positions[i] - pos;
- if (thisdist >= 0 && thisdist < bestdist) { nearest = i; bestdist = thisdist; signeddist = thisdist; }
- if (thisdist < 0 && -thisdist < bestdist) { nearest = i; bestdist = -thisdist; signeddist = thisdist; }
- }
- return signeddist;
- }
-
- /// <summary>
- /// Find the node at position pos, given known positions of several nodes.
- /// </summary>
- /// <param name="pos"></param>
- /// <param name="positions"></param>
- /// <param name="nodes"></param>
- /// <returns></returns>
- Node get(int pos, int[] positions, Node[] nodes)
- {
- int nearest;
- int delta = dist(pos, out nearest, positions);
- Node node = nodes[nearest];
- if (delta > 0)
- for (int i = 0; i < delta; i++)
- node = node.prev;
- else
- for (int i = 0; i > delta; i--)
- node = node.next;
- return node;
- }
-
- /// <summary>
- /// Get nodes at positions p1 and p2, given nodes at several positions.
- /// </summary>
- /// <param name="p1"></param>
- /// <param name="p2"></param>
- /// <param name="n1"></param>
- /// <param name="n2"></param>
- /// <param name="positions"></param>
- /// <param name="nodes"></param>
- void getPair(int p1, int p2, out Node n1, out Node n2, int[] positions, Node[] nodes)
- {
- int nearest1, nearest2;
- int delta1 = dist(p1, out nearest1, positions), d1 = delta1 < 0 ? -delta1 : delta1;
- int delta2 = dist(p2, out nearest2, positions), d2 = delta2 < 0 ? -delta2 : delta2;
-
- if (d1 < d2)
- {
- n1 = get(p1, positions, nodes);
- n2 = get(p2, new int[] { positions[nearest2], p1 }, new Node[] { nodes[nearest2], n1 });
- }
- else
- {
- n2 = get(p2, positions, nodes);
- n1 = get(p1, new int[] { positions[nearest1], p2 }, new Node[] { nodes[nearest1], n2 });
- }
- }
- #endregion
-
- #region Insertion
-#if HASHINDEX
- void insert(int index, Node succ, T item)
- {
- Node newnode = new Node(item);
- if (dict.FindOrAdd(item, ref newnode))
- throw new DuplicateNotAllowedException("Item already in indexed list");
- insertNode(true, succ, newnode);
- }
-
- /// <summary>
- /// Insert a Node before another one. Unchecked version.
- /// </summary>
- /// <param name="succ">The successor to be</param>
- /// <param name="newnode">Node to insert</param>
- /// <param name="updateViews">update overlapping view in this call</param>
- void insertNode(bool updateViews, Node succ, Node newnode)
- {
- newnode.next = succ;
- Node pred = newnode.prev = succ.prev;
- succ.prev.next = newnode;
- succ.prev = newnode;
- size++;
- if (underlying != null)
- underlying.size++;
- settag(newnode);
- if (updateViews)
- fixViewsAfterInsert(succ, pred, 1, 0);
- }
-#else
- /// <summary>
- ///
- /// </summary>
- /// <param name="index">The index in this view</param>
- /// <param name="succ"></param>
- /// <param name="item"></param>
- /// <returns></returns>
- Node insert(int index, Node succ, T item)
- {
- Node newnode = new Node(item, succ.prev, succ);
- succ.prev.next = newnode;
- succ.prev = newnode;
- size++;
- if (underlying != null)
- underlying.size++;
- fixViewsAfterInsert(succ, newnode.prev, 1, Offset + index);
- return newnode;
- }
-#endif
- #endregion
-
- #region Removal
- T remove(Node node, int index)
- {
- fixViewsBeforeSingleRemove(node, Offset + index);
- node.prev.next = node.next;
- node.next.prev = node.prev;
- size--;
- if (underlying != null)
- underlying.size--;
-#if HASHINDEX
- removefromtaggroup(node);
-#endif
- return node.item;
- }
-
-#if HASHINDEX
- private bool dictremove(T item, out Node node)
- {
- if (underlying == null)
- {
- if (!dict.Remove(item, out node))
- return false;
- }
- else
- {
- //We cannot avoid calling dict twice - have to intersperse the listorder test!
- if (!contains(item, out node))
- return false;
- dict.Remove(item);
- }
- return true;
- }
-#endif
- #endregion
-
- #region fixView utilities
- /// <summary>
- ///
- /// </summary>
- /// <param name="added">The actual number of inserted nodes</param>
- /// <param name="pred">The predecessor of the inserted nodes</param>
- /// <param name="succ">The successor of the added nodes</param>
- /// <param name="realInsertionIndex"></param>
- void fixViewsAfterInsert(Node succ, Node pred, int added, int realInsertionIndex)
- {
- if (views != null)
- foreach (HashedLinkedList<T> view in views)
- {
- if (view != this)
- {
-#if HASHINDEX
- if (pred.precedes(view.startsentinel) || (view.startsentinel == pred && view.size > 0))
- view.offset += added;
- if (view.startsentinel.precedes(pred) && succ.precedes(view.endsentinel))
- view.size += added;
- if (view.startsentinel == pred && view.size > 0)
- view.startsentinel = succ.prev;
- if (view.endsentinel == succ)
- view.endsentinel = pred.next;
-#else
- if (view.Offset == realInsertionIndex && view.size > 0)
- view.startsentinel = succ.prev;
- if (view.Offset + view.size == realInsertionIndex)
- view.endsentinel = pred.next;
- if (view.Offset < realInsertionIndex && view.Offset + view.size > realInsertionIndex)
- view.size += added;
- if (view.Offset > realInsertionIndex || (view.Offset == realInsertionIndex && view.size > 0))
- view.offset += added;
-#endif
- }
- }
- }
-
- void fixViewsBeforeSingleRemove(Node node, int realRemovalIndex)
- {
- if (views != null)
- foreach (HashedLinkedList<T> view in views)
- {
- if (view != this)
- {
-#if HASHINDEX
- if (view.startsentinel.precedes(node) && node.precedes(view.endsentinel))
- view.size--;
- if (!view.startsentinel.precedes(node))
- view.offset--;
- if (view.startsentinel == node)
- view.startsentinel = node.prev;
- if (view.endsentinel == node)
- view.endsentinel = node.next;
-#else
- if (view.offset - 1 == realRemovalIndex)
- view.startsentinel = node.prev;
- if (view.offset + view.size == realRemovalIndex)
- view.endsentinel = node.next;
- if (view.offset <= realRemovalIndex && view.offset + view.size > realRemovalIndex)
- view.size--;
- if (view.offset > realRemovalIndex)
- view.offset--;
-#endif
- }
- }
- }
-
-#if HASHINDEX
-#else
- void fixViewsBeforeRemove(int start, int count, Node first, Node last)
- {
- int clearend = start + count - 1;
- if (views != null)
- foreach (HashedLinkedList<T> view in views)
- {
- if (view == this)
- continue;
- int viewoffset = view.Offset, viewend = viewoffset + view.size - 1;
- //sentinels
- if (start < viewoffset && viewoffset - 1 <= clearend)
- view.startsentinel = first.prev;
- if (start <= viewend + 1 && viewend < clearend)
- view.endsentinel = last.next;
- //offsets and sizes
- if (start < viewoffset)
- {
- if (clearend < viewoffset)
- view.offset = viewoffset - count;
- else
- {
- view.offset = start;
- view.size = clearend < viewend ? viewend - clearend : 0;
- }
- }
- else if (start <= viewend)
- view.size = clearend <= viewend ? view.size - count : start - viewoffset;
- }
- }
-#endif
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="otherView"></param>
- /// <returns>The position of View(otherOffset, otherSize) wrt. this view</returns>
- MutualViewPosition viewPosition(HashedLinkedList<T> otherView)
- {
-#if HASHINDEX
- Node otherstartsentinel = otherView.startsentinel, otherendsentinel = otherView.endsentinel,
- first = startsentinel.next, last = endsentinel.prev,
- otherfirst = otherstartsentinel.next, otherlast = otherendsentinel.prev;
- if (last.precedes(otherfirst) || otherlast.precedes(first))
- return MutualViewPosition.NonOverlapping;
- if (size == 0 || (otherstartsentinel.precedes(first) && last.precedes(otherendsentinel)))
- return MutualViewPosition.Contains;
- if (otherView.size == 0 || (startsentinel.precedes(otherfirst) && otherlast.precedes(endsentinel)))
- return MutualViewPosition.ContainedIn;
- return MutualViewPosition.Overlapping;
-#else
- int end = offset + size, otherOffset = otherView.offset, otherSize = otherView.size, otherEnd = otherOffset + otherSize;
- if (otherOffset >= end || otherEnd <= offset)
- return MutualViewPosition.NonOverlapping;
- if (size == 0 || (otherOffset <= offset && end <= otherEnd))
- return MutualViewPosition.Contains;
- if (otherSize == 0 || (offset <= otherOffset && otherEnd <= end))
- return MutualViewPosition.ContainedIn;
- return MutualViewPosition.Overlapping;
-#endif
- }
-
- void disposeOverlappingViews(bool reverse)
- {
- if (views != null)
- {
- foreach (HashedLinkedList<T> view in views)
- {
- if (view != this)
- {
- switch (viewPosition(view))
- {
- case MutualViewPosition.ContainedIn:
- if (reverse)
- { }
- else
- view.Dispose();
- break;
- case MutualViewPosition.Overlapping:
- view.Dispose();
- break;
- case MutualViewPosition.Contains:
- case MutualViewPosition.NonOverlapping:
- break;
- }
- }
- }
- }
- }
-
- #endregion
-
- #endregion
-
- #region Constructors
-
- /// <summary>
- /// Create a linked list with en external item equalityComparer
- /// </summary>
- /// <param name="itemequalityComparer">The external equalityComparer</param>
- public HashedLinkedList(SCG.IEqualityComparer<T> itemequalityComparer)
- : base(itemequalityComparer)
- {
- offset = 0;
- size = stamp = 0;
- startsentinel = new Node(default(T));
- endsentinel = new Node(default(T));
- startsentinel.next = endsentinel;
- endsentinel.prev = startsentinel;
-#if HASHINDEX
- //It is important that the sentinels are different:
- startsentinel.taggroup = new TagGroup();
- startsentinel.taggroup.tag = int.MinValue;
- startsentinel.taggroup.count = 0;
- endsentinel.taggroup = new TagGroup();
- endsentinel.taggroup.tag = int.MaxValue;
- endsentinel.taggroup.count = 0;
- dict = new HashDictionary<T, Node>(itemequalityComparer);
-#endif
- }
-
- /// <summary>
- /// Create a linked list with the natural item equalityComparer
- /// </summary>
- public HashedLinkedList() : this(EqualityComparer<T>.Default) { }
-
- #endregion
-
- #region Node nested class
-
- /// <summary>
- /// An individual cell in the linked list
- /// </summary>
- [Serializable]
- class Node
- {
- public Node prev;
-
- public Node next;
-
- public T item;
-
- #region Tag support
-#if HASHINDEX
- internal int tag;
-
- internal TagGroup taggroup;
-
- internal bool precedes(Node that)
- {
- //Debug.Assert(taggroup != null, "taggroup field null");
- //Debug.Assert(that.taggroup != null, "that.taggroup field null");
- int t1 = taggroup.tag;
- int t2 = that.taggroup.tag;
-
- return t1 < t2 ? true : t1 > t2 ? false : tag < that.tag;
- }
-#endif
- #endregion
-
- [Tested]
- internal Node(T item) { this.item = item; }
-
- [Tested]
- internal Node(T item, Node prev, Node next)
- {
- this.item = item; this.prev = prev; this.next = next;
- }
-
- public override string ToString()
- {
-#if HASHINDEX
- return String.Format("Node: (item={0}, tag={1})", item, tag);
-#else
- return String.Format("Node(item={0})", item);
-#endif
- }
- }
-
- #endregion
-
- #region Taggroup nested class and tag maintenance utilities
-#if HASHINDEX
- /// <summary>
- /// A group of nodes with the same high tag. Purpose is to be
- /// able to tell the sequence order of two nodes without having to scan through
- /// the list.
- /// </summary>
- [Serializable]
- class TagGroup
- {
- internal int tag, count;
-
- internal Node first, last;
-
- /// <summary>
- /// Pretty print a tag group
- /// </summary>
- /// <returns>Formatted tag group</returns>
- public override string ToString()
- { return String.Format("TagGroup(tag={0}, cnt={1}, fst={2}, lst={3})", tag, count, first, last); }
- }
-
- //Constants for tag maintenance
- const int wordsize = 32;
-
- const int lobits = 3;
-
- const int hibits = lobits + 1;
-
- const int losize = 1 << lobits;
-
- const int hisize = 1 << hibits;
-
- const int logwordsize = 5;
-
- TagGroup gettaggroup(Node pred, Node succ, out int lowbound, out int highbound)
- {
- TagGroup predgroup = pred.taggroup, succgroup = succ.taggroup;
-
- if (predgroup == succgroup)
- {
- lowbound = pred.tag + 1;
- highbound = succ.tag - 1;
- return predgroup;
- }
- else if (predgroup.first != null)
- {
- lowbound = pred.tag + 1;
- highbound = int.MaxValue;
- return predgroup;
- }
- else if (succgroup.first != null)
- {
- lowbound = int.MinValue;
- highbound = succ.tag - 1;
- return succgroup;
- }
- else
- {
- lowbound = int.MinValue;
- highbound = int.MaxValue;
- return new TagGroup();
- }
- }
-
-
- /// <summary>
- /// Put a tag on a node (already inserted in the list). Split taggroups and renumber as
- /// necessary.
- /// </summary>
- /// <param name="node">The node to tag</param>
- void settag(Node node)
- {
- Node pred = node.prev, succ = node.next;
- TagGroup predgroup = pred.taggroup, succgroup = succ.taggroup;
-
- if (predgroup == succgroup)
- {
- node.taggroup = predgroup;
- predgroup.count++;
- if (pred.tag + 1 == succ.tag)
- splittaggroup(predgroup);
- else
- node.tag = (pred.tag + 1) / 2 + (succ.tag - 1) / 2;
- }
- else if (predgroup.first != null)
- {
- node.taggroup = predgroup;
- predgroup.last = node;
- predgroup.count++;
- if (pred.tag == int.MaxValue)
- splittaggroup(predgroup);
- else
- node.tag = pred.tag / 2 + int.MaxValue / 2 + 1;
- }
- else if (succgroup.first != null)
- {
- node.taggroup = succgroup;
- succgroup.first = node;
- succgroup.count++;
- if (succ.tag == int.MinValue)
- splittaggroup(node.taggroup);
- else
- node.tag = int.MinValue / 2 + (succ.tag - 1) / 2;
- }
- else
- {
- Debug.Assert(Taggroups == 0);
-
- TagGroup newgroup = new TagGroup();
-
- Taggroups = 1;
- node.taggroup = newgroup;
- newgroup.first = newgroup.last = node;
- newgroup.count = 1;
- return;
- }
- }
-
-
- /// <summary>
- /// Remove a node from its taggroup.
- /// <br/> When this is called, node must already have been removed from the underlying list
- /// </summary>
- /// <param name="node">The node to remove</param>
- void removefromtaggroup(Node node)
- {
-
- TagGroup taggroup = node.taggroup;
-
- if (--taggroup.count == 0)
- {
- Taggroups--;
- return;
- }
-
- if (node == taggroup.first)
- taggroup.first = node.next;
-
- if (node == taggroup.last)
- taggroup.last = node.prev;
-
- //node.taggroup = null;
- if (taggroup.count != losize || Taggroups == 1)
- return;
-
- TagGroup otg;
- // bug20070911:
- Node neighbor;
- if ((neighbor = taggroup.first.prev) != startsentinel
- && (otg = neighbor.taggroup).count <= losize)
- taggroup.first = otg.first;
- else if ((neighbor = taggroup.last.next) != endsentinel
- && (otg = neighbor.taggroup).count <= losize)
- taggroup.last = otg.last;
- else
- return;
-
- Node n = otg.first;
-
- for (int i = 0, length = otg.count; i < length; i++)
- {
- n.taggroup = taggroup;
- n = n.next;
- }
-
- taggroup.count += otg.count;
- Taggroups--;
- n = taggroup.first;
-
- const int ofs = wordsize - hibits;
-
- for (int i = 0, count = taggroup.count; i < count; i++)
- {
- n.tag = (i - losize) << ofs; //(i-8)<<28
- n = n.next;
- }
- }
-
-
- /// <summary>
- /// Split a tag group to make rom for more tags.
- /// </summary>
- /// <param name="taggroup">The tag group</param>
- void splittaggroup(TagGroup taggroup)
- {
- Node n = taggroup.first;
- int ptgt = taggroup.first.prev.taggroup.tag;
- int ntgt = taggroup.last.next.taggroup.tag;
-
- Debug.Assert(ptgt + 1 <= ntgt - 1);
-
- int ofs = wordsize - hibits;
- int newtgs = (taggroup.count - 1) / hisize;
- int tgtdelta = (int)((ntgt + 0.0 - ptgt) / (newtgs + 2)), tgtag = ptgt;
-
- tgtdelta = tgtdelta == 0 ? 1 : tgtdelta;
- for (int j = 0; j < newtgs; j++)
- {
- TagGroup newtaggroup = new TagGroup();
-
- newtaggroup.tag = (tgtag = tgtag >= ntgt - tgtdelta ? ntgt : tgtag + tgtdelta);
- newtaggroup.first = n;
- newtaggroup.count = hisize;
- for (int i = 0; i < hisize; i++)
- {
- n.taggroup = newtaggroup;
- n.tag = (i - losize) << ofs; //(i-8)<<28
- n = n.next;
- }
-
- newtaggroup.last = n.prev;
- }
-
- int rest = taggroup.count - hisize * newtgs;
-
- taggroup.first = n;
- taggroup.count = rest;
- taggroup.tag = (tgtag = tgtag >= ntgt - tgtdelta ? ntgt : tgtag + tgtdelta); ofs--;
- for (int i = 0; i < rest; i++)
- {
- n.tag = (i - hisize) << ofs; //(i-16)<<27
- n = n.next;
- }
-
- taggroup.last = n.prev;
- Taggroups += newtgs;
- if (tgtag == ntgt)
- redistributetaggroups(taggroup);
- }
-
-
- private void redistributetaggroups(TagGroup taggroup)
- {
- TagGroup pred = taggroup, succ = taggroup, tmp;
- double limit = 1, bigt = Math.Pow(Taggroups, 1.0 / 30);//?????
- int bits = 1, count = 1, lowmask = 0, himask = 0, target = 0;
-
- do
- {
- bits++;
- lowmask = (1 << bits) - 1;
- himask = ~lowmask;
- target = taggroup.tag & himask;
- while ((tmp = pred.first.prev.taggroup).first != null && (tmp.tag & himask) == target)
- { count++; pred = tmp; }
-
- while ((tmp = succ.last.next.taggroup).last != null && (tmp.tag & himask) == target)
- { count++; succ = tmp; }
-
- limit *= bigt;
- } while (count > limit);
-
- //redistibute tags
- int lob = pred.first.prev.taggroup.tag, upb = succ.last.next.taggroup.tag;
- int delta = upb / (count + 1) - lob / (count + 1);
-
- Debug.Assert(delta > 0);
- for (int i = 0; i < count; i++)
- {
- pred.tag = lob + (i + 1) * delta;
- pred = pred.last.next.taggroup;
- }
- }
-#endif
-
- #endregion
-
- #region Position, PositionComparer and ViewHandler nested types
- class PositionComparer : SCG.IComparer<Position>
- {
- static PositionComparer _default;
- PositionComparer() { }
- public static PositionComparer Default { get { return _default ?? (_default = new PositionComparer()); } }
- public int Compare(Position a, Position b)
- {
-#if HASHINDEX
- return a.Endpoint == b.Endpoint ? 0 : a.Endpoint.precedes(b.Endpoint) ? -1 : 1;
-#else
- return a.Index.CompareTo(b.Index);
-#endif
- }
- }
- /// <summary>
- /// During RemoveAll, we need to cache the original endpoint indices of views
- /// </summary>
- struct Position
- {
- public readonly HashedLinkedList<T> View;
- public bool Left;
-#if HASHINDEX
- public readonly Node Endpoint;
-#else
- public readonly int Index;
-#endif
- public Position(HashedLinkedList<T> view, bool left)
- {
- View = view;
- Left = left;
-#if HASHINDEX
- Endpoint = left ? view.startsentinel.next : view.endsentinel.prev;
-#else
- Index = left ? view.Offset : view.Offset + view.size - 1;
-#endif
- }
-#if HASHINDEX
- public Position(Node node, int foo) { this.Endpoint = node; View = null; Left = false; }
-#else
- public Position(int index) { this.Index = index; View = null; Left = false; }
-#endif
- }
-
- //TODO: merge the two implementations using Position values as arguments
- /// <summary>
- /// Handle the update of (other) views during a multi-remove operation.
- /// </summary>
- struct ViewHandler
- {
- ArrayList<Position> leftEnds;
- ArrayList<Position> rightEnds;
- int leftEndIndex, rightEndIndex, leftEndIndex2, rightEndIndex2;
- internal readonly int viewCount;
- internal ViewHandler(HashedLinkedList<T> list)
- {
- leftEndIndex = rightEndIndex = leftEndIndex2 = rightEndIndex2 = viewCount = 0;
- leftEnds = rightEnds = null;
- if (list.views != null)
- foreach (HashedLinkedList<T> v in list.views)
- if (v != list)
- {
- if (leftEnds == null)
- {
- leftEnds = new ArrayList<Position>();
- rightEnds = new ArrayList<Position>();
- }
- leftEnds.Add(new Position(v, true));
- rightEnds.Add(new Position(v, false));
- }
- if (leftEnds == null)
- return;
- viewCount = leftEnds.Count;
- leftEnds.Sort(PositionComparer.Default);
- rightEnds.Sort(PositionComparer.Default);
- }
-#if HASHINDEX
- internal void skipEndpoints(int removed, Node n)
- {
- if (viewCount > 0)
- {
- Position endpoint;
- while (leftEndIndex < viewCount && ((endpoint = leftEnds[leftEndIndex]).Endpoint.prev.precedes(n)))
- {
- HashedLinkedList<T> view = endpoint.View;
- view.offset = view.offset - removed;//TODO: extract offset.Value?
- view.size += removed;
- leftEndIndex++;
- }
- while (rightEndIndex < viewCount && (endpoint = rightEnds[rightEndIndex]).Endpoint.precedes(n))
- {
- HashedLinkedList<T> view = endpoint.View;
- view.size -= removed;
- rightEndIndex++;
- }
- }
- if (viewCount > 0)
- {
- Position endpoint;
- while (leftEndIndex2 < viewCount && (endpoint = leftEnds[leftEndIndex2]).Endpoint.prev.precedes(n))
- leftEndIndex2++;
- while (rightEndIndex2 < viewCount && (endpoint = rightEnds[rightEndIndex2]).Endpoint.next.precedes(n))
- rightEndIndex2++;
- }
- }
- /// <summary>
- /// To be called with n pointing to the right of each node to be removed in a stretch.
- /// And at the endsentinel.
- ///
- /// Update offset of a view whose left endpoint (has not already been handled and) is n or precedes n.
- /// I.e. startsentinel precedes n.
- /// Also update the size as a prelude to handling the right endpoint.
- ///
- /// Update size of a view not already handled and whose right endpoint precedes n.
- /// </summary>
- /// <param name="removed">The number of nodes left of n to be removed</param>
- /// <param name="n"></param>
- internal void updateViewSizesAndCounts(int removed, Node n)
- {
- if (viewCount > 0)
- {
- Position endpoint;
- while (leftEndIndex < viewCount && ((endpoint = leftEnds[leftEndIndex]).Endpoint.prev.precedes(n)))
- {
- HashedLinkedList<T> view = endpoint.View;
- view.offset = view.offset - removed; //TODO: fix use of offset
- view.size += removed;
- leftEndIndex++;
- }
- while (rightEndIndex < viewCount && (endpoint = rightEnds[rightEndIndex]).Endpoint.precedes(n))
- {
- HashedLinkedList<T> view = endpoint.View;
- view.size -= removed;
- rightEndIndex++;
- }
- }
- }
- /// <summary>
- /// To be called with n being the first not-to-be-removed node after a (stretch of) node(s) to be removed.
- ///
- /// It will update the startsentinel of views (that have not been handled before and)
- /// whose startsentinel precedes n, i.e. is to be deleted.
- ///
- /// It will update the endsentinel of views (...) whose endsentinel precedes n, i.e. is to be deleted.
- ///
- /// PROBLEM: DOESNT WORK AS ORIGINALLY ADVERTISED. WE MUST DO THIS BEFORE WE ACTUALLY REMOVE THE NODES. WHEN THE
- /// NODES HAVE BEEN REMOVED, THE precedes METHOD WILL NOT WORK!
- /// </summary>
- /// <param name="n"></param>
- /// <param name="newstart"></param>
- /// <param name="newend"></param>
- internal void updateSentinels(Node n, Node newstart, Node newend)
- {
- if (viewCount > 0)
- {
- Position endpoint;
- while (leftEndIndex2 < viewCount && (endpoint = leftEnds[leftEndIndex2]).Endpoint.prev.precedes(n))
- {
- HashedLinkedList<T> view = endpoint.View;
- view.startsentinel = newstart;
- leftEndIndex2++;
- }
- while (rightEndIndex2 < viewCount && (endpoint = rightEnds[rightEndIndex2]).Endpoint.next.precedes(n))
- {
- HashedLinkedList<T> view = endpoint.View;
- view.endsentinel = newend;
- rightEndIndex2++;
- }
- }
- }
-#else
- /// <summary>
- /// This is to be called with realindex pointing to the first node to be removed after a (stretch of) node that was not removed
- /// </summary>
- /// <param name="removed"></param>
- /// <param name="realindex"></param>
- internal void skipEndpoints(int removed, int realindex)
- {
- if (viewCount > 0)
- {
- Position endpoint;
- while (leftEndIndex < viewCount && (endpoint = leftEnds[leftEndIndex]).Index <= realindex)
- {
- HashedLinkedList<T> view = endpoint.View;
- view.offset = view.offset - removed;
- view.size += removed;
- leftEndIndex++;
- }
- while (rightEndIndex < viewCount && (endpoint = rightEnds[rightEndIndex]).Index < realindex)
- {
- HashedLinkedList<T> view = endpoint.View;
- view.size -= removed;
- rightEndIndex++;
- }
- }
- if (viewCount > 0)
- {
- Position endpoint;
- while (leftEndIndex2 < viewCount && (endpoint = leftEnds[leftEndIndex2]).Index <= realindex)
- leftEndIndex2++;
- while (rightEndIndex2 < viewCount && (endpoint = rightEnds[rightEndIndex2]).Index < realindex - 1)
- rightEndIndex2++;
- }
- }
- internal void updateViewSizesAndCounts(int removed, int realindex)
- {
- if (viewCount > 0)
- {
- Position endpoint;
- while (leftEndIndex < viewCount && (endpoint = leftEnds[leftEndIndex]).Index <= realindex)
- {
- HashedLinkedList<T> view = endpoint.View;
- view.offset = view.Offset - removed;
- view.size += removed;
- leftEndIndex++;
- }
- while (rightEndIndex < viewCount && (endpoint = rightEnds[rightEndIndex]).Index < realindex)
- {
- HashedLinkedList<T> view = endpoint.View;
- view.size -= removed;
- rightEndIndex++;
- }
- }
- }
- internal void updateSentinels(int realindex, Node newstart, Node newend)
- {
- if (viewCount > 0)
- {
- Position endpoint;
- while (leftEndIndex2 < viewCount && (endpoint = leftEnds[leftEndIndex2]).Index <= realindex)
- {
- HashedLinkedList<T> view = endpoint.View;
- view.startsentinel = newstart;
- leftEndIndex2++;
- }
- while (rightEndIndex2 < viewCount && (endpoint = rightEnds[rightEndIndex2]).Index < realindex - 1)
- {
- HashedLinkedList<T> view = endpoint.View;
- view.endsentinel = newend;
- rightEndIndex2++;
- }
- }
- }
-#endif
- }
- #endregion
-
- #region Range nested class
-
- class Range : DirectedCollectionValueBase<T>, IDirectedCollectionValue<T>
- {
- int start, count, rangestamp;
- Node startnode, endnode;
-
- HashedLinkedList<T> list;
-
- bool forwards;
-
-
- internal Range(HashedLinkedList<T> list, int start, int count, bool forwards)
- {
- this.list = list; this.rangestamp = list.underlying != null ? list.underlying.stamp : list.stamp;
- this.start = start; this.count = count; this.forwards = forwards;
- if (count > 0)
- {
- startnode = list.get(start);
- endnode = list.get(start + count - 1);
- }
- }
-
- public override bool IsEmpty { get { list.modifycheck(rangestamp); return count == 0; } }
-
- [Tested]
- public override int Count { [Tested]get { list.modifycheck(rangestamp); return count; } }
-
-
- public override Speed CountSpeed { get { list.modifycheck(rangestamp); return Speed.Constant; } }
-
-
- public override T Choose()
- {
- list.modifycheck(rangestamp);
- if (count > 0) return startnode.item;
- throw new NoSuchItemException();
- }
-
-
- [Tested]
- public override SCG.IEnumerator<T> GetEnumerator()
- {
- int togo = count;
-
- list.modifycheck(rangestamp);
- if (togo == 0)
- yield break;
-
- Node cursor = forwards ? startnode : endnode;
-
- yield return cursor.item;
- while (--togo > 0)
- {
- cursor = forwards ? cursor.next : cursor.prev;
- list.modifycheck(rangestamp);
- yield return cursor.item;
- }
- }
-
-
- [Tested]
- public override IDirectedCollectionValue<T> Backwards()
- {
- list.modifycheck(rangestamp);
-
- Range b = (Range)MemberwiseClone();
-
- b.forwards = !forwards;
- return b;
- }
-
-
- [Tested]
- IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return Backwards(); }
-
-
- [Tested]
- public override EnumerationDirection Direction
- {
- [Tested]
- get
- { return forwards ? EnumerationDirection.Forwards : EnumerationDirection.Backwards; }
- }
- }
-
-
- #endregion
-
- #region IDisposable Members
-
- /// <summary>
- /// Invalidate this list. If a view, just invalidate the view.
- /// If not a view, invalidate the list and all views on it.
- /// </summary>
- public virtual void Dispose()
- {
- Dispose(false);
- }
-
- void Dispose(bool disposingUnderlying)
- {
- if (isValid)
- {
- if (underlying != null)
- {
- isValid = false;
- if (!disposingUnderlying && views != null)
- views.Remove(myWeakReference);
- endsentinel = null;
- startsentinel = null;
- underlying = null;
- views = null;
- myWeakReference = null;
- }
- else
- {
- //isValid = false;
- //endsentinel = null;
- //startsentinel = null;
- if (views != null)
- foreach (HashedLinkedList<T> view in views)
- view.Dispose(true);
- //views = null;
- Clear();
- }
- }
- }
-
- #endregion IDisposable stuff
-
- #region IList<T> Members
-
- /// <summary>
- /// </summary>
- /// <exception cref="NoSuchItemException"> if this list is empty.</exception>
- /// <value>The first item in this list.</value>
- [Tested]
- public virtual T First
- {
- [Tested]
- get
- {
- validitycheck();
- if (size == 0)
- throw new NoSuchItemException();
- return startsentinel.next.item;
- }
- }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="NoSuchItemException"> if this list is empty.</exception>
- /// <value>The last item in this list.</value>
- [Tested]
- public virtual T Last
- {
- [Tested]
- get
- {
- validitycheck();
- if (size == 0)
- throw new NoSuchItemException();
- return endsentinel.prev.item;
- }
- }
-
- /// <summary>
- /// Since <code>Add(T item)</code> always add at the end of the list,
- /// this describes if list has FIFO or LIFO semantics.
- /// </summary>
- /// <value>True if the <code>Remove()</code> operation removes from the
- /// start of the list, false if it removes from the end. THe default for a new linked list is true.</value>
- [Tested]
- public virtual bool FIFO
- {
- [Tested]
- get { validitycheck(); return fIFO; }
- [Tested]
- set { updatecheck(); fIFO = value; }
- }
-
- /// <summary>
- ///
- /// </summary>
- public virtual bool IsFixedSize
- {
- get { validitycheck(); return false; }
- }
-
- /// <summary>
- /// On this list, this indexer is read/write.
- /// <exception cref="IndexOutOfRangeException"/> if i is negative or
- /// >= the size of the collection.
- /// </summary>
- /// <value>The i'th item of this list.</value>
- /// <param name="index">The index of the item to fetch or store.</param>
- [Tested]
- public virtual T this[int index]
- {
- [Tested]
- get { validitycheck(); return get(index).item; }
- [Tested]
- set
- {
- updatecheck();
- Node n = get(index);
- //
- T item = n.item;
-#if HASHINDEX
-
- if (itemequalityComparer.Equals(value, item))
- {
- n.item = value;
- dict.Update(value, n);
- }
- else if (!dict.FindOrAdd(value, ref n))
- {
- dict.Remove(item);
- n.item = value;
- }
- else
- throw new ArgumentException("Item already in indexed list");
-#else
- n.item = value;
-#endif
- (underlying ?? this).raiseForSetThis(index, value, item);
- }
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public virtual Speed IndexingSpeed { get { return Speed.Linear; } }
-
- /// <summary>
- /// Insert an item at a specific index location in this list.
- /// <exception cref="IndexOutOfRangeException"/> if i is negative or
- /// > the size of the collection.</summary>
- /// <param name="i">The index at which to insert.</param>
- /// <param name="item">The item to insert.</param>
- [Tested]
- public virtual void Insert(int i, T item)
- {
- updatecheck();
- insert(i, i == size ? endsentinel : get(i), item);
- if (ActiveEvents != EventTypeEnum.None)
- (underlying ?? this).raiseForInsert(i + Offset, item);
- }
-
- /// <summary>
- /// Insert an item at the end of a compatible view, used as a pointer.
- /// <para>The <code>pointer</code> must be a view on the same list as
- /// <code>this</code> and the endpoitn of <code>pointer</code> must be
- /// a valid insertion point of <code>this</code></para>
- /// </summary>
- /// <exception cref="IncompatibleViewException">If <code>pointer</code>
- /// is not a view on the same list as <code>this</code></exception>
- /// <exception cref="IndexOutOfRangeException"><b>??????</b> if the endpoint of
- /// <code>pointer</code> is not inside <code>this</code></exception>
- /// <exception cref="DuplicateNotAllowedException"> if the list has
- /// <code>AllowsDuplicates==false</code> and the item is
- /// already in the list.</exception>
- /// <param name="pointer"></param>
- /// <param name="item"></param>
- public void Insert(IList<T> pointer, T item)
- {
- updatecheck();
- if ((pointer == null) || ((pointer.Underlying ?? pointer) != (underlying ?? this)))
- throw new IncompatibleViewException();
-#warning INEFFICIENT
- //TODO: make this efficient (the whole point of the method:
- //Do NOT use Insert, but insert the node at pointer.endsentinel, checking
- //via the ordering that this is a valid insertion point
- Insert(pointer.Offset + pointer.Count - Offset, item);
- }
-
- /// <summary>
- /// Insert into this list all items from an enumerable collection starting
- /// at a particular index.
- /// <exception cref="IndexOutOfRangeException"/> if i is negative or
- /// > the size of the collection.
- /// </summary>
- /// <param name="i">Index to start inserting at</param>
- /// <param name="items">Items to insert</param>
- /// <typeparam name="U"></typeparam>
- [Tested]
- public virtual void InsertAll<U>(int i, SCG.IEnumerable<U> items) where U : T
- {
- insertAll(i, items, true);
- }
-
- void insertAll<U>(int i, SCG.IEnumerable<U> items, bool insertion) where U : T
- {
- updatecheck();
- Node succ, node, pred;
- int count = 0;
- succ = i == size ? endsentinel : get(i);
- pred = node = succ.prev;
-#if HASHINDEX
- TagGroup taggroup = null;
- int taglimit = 0, thetag = 0;
- taggroup = gettaggroup(node, succ, out thetag, out taglimit);
- try
- {
- foreach (T item in items)
- {
- Node tmp = new Node(item, node, null);
- if (!dict.FindOrAdd(item, ref tmp))
- {
- tmp.tag = thetag < taglimit ? ++thetag : thetag;
- tmp.taggroup = taggroup;
- node.next = tmp;
- count++;
- node = tmp;
- }
- else
- throw new DuplicateNotAllowedException("Item already in indexed list");
- }
- }
- finally
- {
- if (count != 0)
- {
- taggroup.count += count;
- if (taggroup != pred.taggroup)
- taggroup.first = pred.next;
- if (taggroup != succ.taggroup)
- taggroup.last = node;
- succ.prev = node;
- node.next = succ;
- if (node.tag == node.prev.tag)
- splittaggroup(taggroup);
- size += count;
- if (underlying != null)
- underlying.size += count;
- fixViewsAfterInsert(succ, pred, count, 0);
- raiseForInsertAll(pred, i, count, insertion);
- }
- }
-#else
- foreach (T item in items)
- {
- Node tmp = new Node(item, node, null);
- node.next = tmp;
- count++;
- node = tmp;
- }
- if (count == 0)
- return;
- succ.prev = node;
- node.next = succ;
- size += count;
- if (underlying != null)
- underlying.size += count;
- if (count > 0)
- {
- fixViewsAfterInsert(succ, pred, count, offset + i);
- raiseForInsertAll(pred, i, count, insertion);
- }
-#endif
- }
-
- private void raiseForInsertAll(Node node, int i, int added, bool insertion)
- {
- if (ActiveEvents != 0)
- {
- int index = Offset + i;
- if ((ActiveEvents & (EventTypeEnum.Added | EventTypeEnum.Inserted)) != 0)
- for (int j = index; j < index + added; j++)
- {
-#warning must we check stamps here?
- node = node.next;
- T item = node.item;
- if (insertion) raiseItemInserted(item, j);
- raiseItemsAdded(item, 1);
- }
- raiseCollectionChanged();
- }
- }
-
- /// <summary>
- /// Insert an item at the front of this list.
- /// </summary>
- /// <param name="item">The item to insert.</param>
- [Tested]
- public virtual void InsertFirst(T item)
- {
- updatecheck();
- insert(0, startsentinel.next, item);
- if (ActiveEvents != EventTypeEnum.None)
- (underlying ?? this).raiseForInsert(0 + Offset, item);
- }
-
- /// <summary>
- /// Insert an item at the back of this list.
- /// </summary>
- /// <param name="item">The item to insert.</param>
- [Tested]
- public virtual void InsertLast(T item)
- {
- updatecheck();
- insert(size, endsentinel, item);
- if (ActiveEvents != EventTypeEnum.None)
- (underlying ?? this).raiseForInsert(size - 1 + Offset, item);
- }
-
- /// <summary>
- /// Create a new list consisting of the results of mapping all items of this
- /// list.
- /// </summary>
- /// <param name="mapper">The delegate defining the map.</param>
- /// <returns>The new list.</returns>
- [Tested]
- public IList<V> Map<V>(Fun<T, V> mapper)
- {
- validitycheck();
-
- HashedLinkedList<V> retval = new HashedLinkedList<V>();
- return map<V>(mapper, retval);
- }
-
- /// <summary>
- /// Create a new list consisting of the results of mapping all items of this
- /// list. The new list will use a specified equalityComparer for the item type.
- /// </summary>
- /// <typeparam name="V">The type of items of the new list</typeparam>
- /// <param name="mapper">The delegate defining the map.</param>
- /// <param name="equalityComparer">The equalityComparer to use for the new list</param>
- /// <returns>The new list.</returns>
- public IList<V> Map<V>(Fun<T, V> mapper, SCG.IEqualityComparer<V> equalityComparer)
- {
- validitycheck();
-
- HashedLinkedList<V> retval = new HashedLinkedList<V>(equalityComparer);
- return map<V>(mapper, retval);
- }
-
- private IList<V> map<V>(Fun<T, V> mapper, HashedLinkedList<V> retval)
- {
- if (size == 0)
- return retval;
- int stamp = this.stamp;
- Node cursor = startsentinel.next;
- HashedLinkedList<V>.Node mcursor = retval.startsentinel;
-
-#if HASHINDEX
- double tagdelta = int.MaxValue / (size + 1.0);
- int count = 1;
- HashedLinkedList<V>.TagGroup taggroup = null;
- taggroup = new HashedLinkedList<V>.TagGroup();
- retval.taggroups = 1;
- taggroup.count = size;
-#endif
- while (cursor != endsentinel)
- {
- V v = mapper(cursor.item);
- modifycheck(stamp);
- mcursor.next = new HashedLinkedList<V>.Node(v, mcursor, null);
- cursor = cursor.next;
- mcursor = mcursor.next;
-#if HASHINDEX
- retval.dict.Add(v, mcursor);
- mcursor.taggroup = taggroup;
- mcursor.tag = (int)(tagdelta * count++);
-#endif
- }
-
-#if HASHINDEX
- taggroup.first = retval.startsentinel.next;
- taggroup.last = mcursor;
-#endif
- retval.endsentinel.prev = mcursor;
- mcursor.next = retval.endsentinel;
- retval.size = size;
- return retval;
- }
-
- /// <summary>
- /// Remove one item from the list: from the front if <code>FIFO</code>
- /// is true, else from the back.
- /// <exception cref="NoSuchItemException"/> if this list is empty.
- /// </summary>
- /// <returns>The removed item.</returns>
- [Tested]
- public virtual T Remove()
- {
- updatecheck();
- if (size == 0)
- throw new NoSuchItemException("List is empty");
- T item = fIFO ? remove(startsentinel.next, 0) : remove(endsentinel.prev, size - 1);
-#if HASHINDEX
- dict.Remove(item);
-#endif
- (underlying ?? this).raiseForRemove(item);
- return item;
- }
-
- /// <summary>
- /// Remove one item from the front of the list.
- /// <exception cref="NoSuchItemException"/> if this list is empty.
- /// </summary>
- /// <returns>The removed item.</returns>
- [Tested]
- public virtual T RemoveFirst()
- {
- updatecheck();
- if (size == 0)
- throw new NoSuchItemException("List is empty");
-
- T item = remove(startsentinel.next, 0);
-#if HASHINDEX
- dict.Remove(item);
-#endif
- if (ActiveEvents != EventTypeEnum.None)
- (underlying ?? this).raiseForRemoveAt(Offset, item);
- return item;
- }
-
- /// <summary>
- /// Remove one item from the back of the list.
- /// <exception cref="NoSuchItemException"/> if this list is empty.
- /// </summary>
- /// <returns>The removed item.</returns>
- [Tested]
- public virtual T RemoveLast()
- {
- updatecheck();
- if (size == 0)
- throw new NoSuchItemException("List is empty");
-
- T item = remove(endsentinel.prev, size - 1);
-#if HASHINDEX
- dict.Remove(item);
-#endif
- if (ActiveEvents != EventTypeEnum.None)
- (underlying ?? this).raiseForRemoveAt(size + Offset, item);
- return item;
- }
-
- /// <summary>
- /// Create a list view on this list.
- /// </summary>
- /// <exception cref="ArgumentOutOfRangeException"> if the start or count is negative</exception>
- /// <exception cref="ArgumentException"> if the range does not fit within list.</exception>
- /// <param name="start">The index in this list of the start of the view.</param>
- /// <param name="count">The size of the view.</param>
- /// <returns>The new list view.</returns>
- [Tested]
- public virtual IList<T> View(int start, int count)
- {
- checkRange(start, count);
- validitycheck();
- if (views == null)
- views = new WeakViewList<HashedLinkedList<T>>();
- HashedLinkedList<T> retval = (HashedLinkedList<T>)MemberwiseClone();
- retval.underlying = underlying != null ? underlying : this;
- retval.offset = offset + start;
- retval.size = count;
- getPair(start - 1, start + count, out retval.startsentinel, out retval.endsentinel,
- new int[] { -1, size }, new Node[] { startsentinel, endsentinel });
- //retval.startsentinel = start == 0 ? startsentinel : get(start - 1);
- //retval.endsentinel = start + count == size ? endsentinel : get(start + count);
-
- //TODO: for the purpose of Dispose, we need to retain a ref to the node
- retval.myWeakReference = views.Add(retval);
- return retval;
- }
-
- /// <summary>
- /// Create a list view on this list containing the (first) occurrence of a particular item.
- /// </summary>
- /// <exception cref="ArgumentException"> if the item is not in this list.</exception>
- /// <param name="item">The item to find.</param>
- /// <returns>The new list view.</returns>
- public virtual IList<T> ViewOf(T item)
- {
-#if HASHINDEX
- Node n;
- validitycheck();
- if (!contains(item, out n))
- return null;
- HashedLinkedList<T> retval = (HashedLinkedList<T>)MemberwiseClone();
- retval.underlying = underlying != null ? underlying : this;
- retval.offset = null;
- retval.startsentinel = n.prev;
- retval.endsentinel = n.next;
- retval.size = 1;
- return retval;
-#else
- int index = 0;
- Node n = startsentinel.next;
- if (!find(item, ref n, ref index))
- return null;
- //TODO: optimize with getpair!
- return View(index, 1);
-#endif
- }
-
- /// <summary>
- /// Create a list view on this list containing the last occurrence of a particular item.
- /// <exception cref="ArgumentException"/> if the item is not in this list.
- /// </summary>
- /// <param name="item">The item to find.</param>
- /// <returns>The new list view.</returns>
- public virtual IList<T> LastViewOf(T item)
- {
-#if HASHINDEX
- return ViewOf(item);
-#else
- int index = size - 1;
- Node n = endsentinel.prev;
- if (!dnif(item, ref n, ref index))
- return null;
- return View(index, 1);
-#endif
- }
-
- /// <summary>
- /// Null if this list is not a view.
- /// </summary>
- /// <value>Underlying list for view.</value>
- [Tested]
- public virtual IList<T> Underlying { [Tested]get { validitycheck(); return underlying; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public virtual bool IsValid { get { return isValid; } }
-
- /// <summary>
- /// </summary>
- /// <value>Offset for this list view or 0 for a underlying list.</value>
- [Tested]
- public virtual int Offset
- {
- [Tested]
- get
- {
- validitycheck();
-#if HASHINDEX
- if (offset == null && underlying != null)
- {
- //TODO: search from both ends simultaneously!
- Node n = underlying.startsentinel;
- int i = 0;
- while (n != startsentinel) { n = n.next; i++; }
- offset = i;
- }
-#endif
- return (int)offset;
- }
- }
-
- /// <summary>
- /// Slide this list view along the underlying list.
- /// </summary>
- /// <exception cref="NotAViewException"> if this list is not a view.</exception>
- /// <exception cref="ArgumentOutOfRangeException"> if the operation
- /// would bring either end of the view outside the underlying list.</exception>
- /// <param name="offset">The signed amount to slide: positive to slide
- /// towards the end.</param>
- [Tested]
- public IList<T> Slide(int offset)
- {
- if (!TrySlide(offset, size))
- throw new ArgumentOutOfRangeException();
- return this;
- }
-
- //TODO: more test cases
- /// <summary>
- /// Slide this list view along the underlying list, perhaps changing its size.
- /// </summary>
- /// <exception cref="NotAViewException"> if this list is not a view.</exception>
- /// <exception cref="ArgumentOutOfRangeException"> if the operation
- /// would bring either end of the view outside the underlying list.</exception>
- /// <param name="offset">The signed amount to slide: positive to slide
- /// towards the end.</param>
- /// <param name="size">The new size of the view.</param>
- public IList<T> Slide(int offset, int size)
- {
- if (!TrySlide(offset, size))
- throw new ArgumentOutOfRangeException();
- return this;
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="offset"></param>
- /// <returns></returns>
- public virtual bool TrySlide(int offset) { return TrySlide(offset, size); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="offset"></param>
- /// <param name="size"></param>
- /// <returns></returns>
- public virtual bool TrySlide(int offset, int size)
- {
- updatecheck();
- if (underlying == null)
- throw new NotAViewException("List not a view");
-
-#pragma warning disable 472
- if (this.offset == null) //Note: only possible with HASHINDEX
-#pragma warning restore 472
- {
-#pragma warning disable 162
- try
- {
- getPair(offset - 1, offset + size, out startsentinel, out endsentinel,
- new int[] { -1, this.size }, new Node[] { startsentinel, endsentinel });
- //TODO: maybe-update offset field
- }
- catch (NullReferenceException)
- {
- return false;
- }
-#pragma warning restore 162
- }
- else
- {
- if (offset + this.offset < 0 || offset + this.offset + size > underlying.size)
- return false;
- int oldoffset = (int)(this.offset);
- getPair(offset - 1, offset + size, out startsentinel, out endsentinel,
- new int[] { -oldoffset - 1, -1, this.size, underlying.size - oldoffset },
- new Node[] { underlying.startsentinel, startsentinel, endsentinel, underlying.endsentinel });
- }
- this.size = size;
- this.offset += offset;
- return true;
- }
-
-
- //TODO: improve the complexity of the implementation
- /// <summary>
- ///
- /// <para>Returns null if <code>otherView</code> is strictly to the left of this view</para>
- /// </summary>
- /// <param name="otherView"></param>
- /// <exception cref="IncompatibleViewException">If otherView does not have the same underlying list as this</exception>
- /// <returns></returns>
- public virtual IList<T> Span(IList<T> otherView)
- {
- if ((otherView == null) || ((otherView.Underlying ?? otherView) != (underlying ?? this)))
- throw new IncompatibleViewException();
- if (otherView.Offset + otherView.Count - Offset < 0)
- return null;
- return (underlying ?? this).View(Offset, otherView.Offset + otherView.Count - Offset);
- }
-
-
- //Question: should we swap items or move nodes around?
- //The first seems much more efficient unless the items are value types
- //with a large memory footprint.
- //(Swapping will do count*3/2 T assignments, linking around will do
- // 4*count ref assignments; note that ref assignments are more expensive
- //than copying non-ref bits)
- /// <summary>
- /// Reverse the list so the items are in the opposite sequence order.
- /// </summary>
- [Tested]
- public virtual void Reverse()
- {
- updatecheck();
- if (size == 0)
- return;
-
- Position[] positions = null;
- int poslow = 0, poshigh = 0;
- if (views != null)
- {
- CircularQueue<Position> _positions = null;
- foreach (HashedLinkedList<T> view in views)
- {
- if (view != this)
- {
- switch (viewPosition(view))
- {
- case MutualViewPosition.ContainedIn:
- (_positions ?? (_positions = new CircularQueue<Position>())).Enqueue(new Position(view, true));
- _positions.Enqueue(new Position(view, false));
- break;
- case MutualViewPosition.Overlapping:
- view.Dispose();
- break;
- case MutualViewPosition.Contains:
- case MutualViewPosition.NonOverlapping:
- break;
- }
- }
- }
- if (_positions != null)
- {
- positions = _positions.ToArray();
- Sorting.IntroSort<Position>(positions, 0, positions.Length, PositionComparer.Default);
- poshigh = positions.Length - 1;
- }
- }
-
- Node a = get(0), b = get(size - 1);
- for (int i = 0; i < size / 2; i++)
- {
- T swap;
- swap = a.item; a.item = b.item; b.item = swap;
-#if HASHINDEX
- dict[a.item] = a; dict[b.item] = b;
-#endif
- if (positions != null)
- mirrorViewSentinelsForReverse(positions, ref poslow, ref poshigh, a, b, i);
- a = a.next; b = b.prev;
- }
- if (positions != null && size % 2 != 0)
- mirrorViewSentinelsForReverse(positions, ref poslow, ref poshigh, a, b, size / 2);
- (underlying ?? this).raiseCollectionChanged();
- }
-
- private void mirrorViewSentinelsForReverse(Position[] positions, ref int poslow, ref int poshigh, Node a, Node b, int i)
- {
-#if HASHINDEX
- int? aindex = offset + i, bindex = offset + size - 1 - i;
-#else
- int aindex = offset + i, bindex = offset + size - 1 - i;
-#endif
- Position pos;
-#if HASHINDEX
- while (poslow <= poshigh && (pos = positions[poslow]).Endpoint == a)
-#else
- while (poslow <= poshigh && (pos = positions[poslow]).Index == aindex)
-#endif
- {
- //TODO: Note: in the case og hashed linked list, if this.offset == null, but pos.View.offset!=null
- //we may at this point compute this.offset and non-null values of aindex and bindex
- if (pos.Left)
- pos.View.endsentinel = b.next;
- else
- {
- pos.View.startsentinel = b.prev;
- pos.View.offset = bindex;
- }
- poslow++;
- }
-#if HASHINDEX
- while (poslow < poshigh && (pos = positions[poshigh]).Endpoint == b)
-#else
- while (poslow < poshigh && (pos = positions[poshigh]).Index == bindex)
-#endif
- {
- if (pos.Left)
- pos.View.endsentinel = a.next;
- else
- {
- pos.View.startsentinel = a.prev;
- pos.View.offset = aindex;
- }
- poshigh--;
- }
- }
-
- /// <summary>
- /// Check if this list is sorted according to the default sorting order
- /// for the item type T, as defined by the <see cref="T:C5.Comparer`1"/> class
- /// </summary>
- /// <exception cref="NotComparableException">if T is not comparable</exception>
- /// <returns>True if the list is sorted, else false.</returns>
- public bool IsSorted() { return IsSorted(Comparer<T>.Default); }
-
- /// <summary>
- /// Check if this list is sorted according to a specific sorting order.
- /// </summary>
- /// <param name="c">The comparer defining the sorting order.</param>
- /// <returns>True if the list is sorted, else false.</returns>
- [Tested]
- public virtual bool IsSorted(SCG.IComparer<T> c)
- {
- validitycheck();
- if (size <= 1)
- return true;
-
- Node node = startsentinel.next;
- T prevItem = node.item;
-
- node = node.next;
- while (node != endsentinel)
- {
- if (c.Compare(prevItem, node.item) > 0)
- return false;
- else
- {
- prevItem = node.item;
- node = node.next;
- }
- }
-
- return true;
- }
-
- /// <summary>
- /// Sort the items of the list according to the default sorting order
- /// for the item type T, as defined by the Comparer[T] class.
- /// (<see cref="T:C5.Comparer`1"/>).
- /// The sorting is stable.
- /// </summary>
- /// <exception cref="InvalidOperationException">if T is not comparable</exception>
- public virtual void Sort() { Sort(Comparer<T>.Default); }
-
- // Sort the linked list using mergesort
- /// <summary>
- /// Sort the items of the list according to a specific sorting order.
- /// The sorting is stable.
- /// </summary>
- /// <param name="c">The comparer defining the sorting order.</param>
- [Tested]
- public virtual void Sort(SCG.IComparer<T> c)
- {
- updatecheck();
- if (size == 0)
- return;
- disposeOverlappingViews(false);
-#if HASHINDEX
- if (underlying != null)
- {
- Node cursor = startsentinel.next;
- while (cursor != endsentinel)
- {
- cursor.taggroup.count--;
- cursor = cursor.next;
- }
- }
-#endif
- // Build a linked list of non-empty runs.
- // The prev field in first node of a run points to next run's first node
- Node runTail = startsentinel.next;
- Node prevNode = startsentinel.next;
-
- endsentinel.prev.next = null;
- while (prevNode != null)
- {
- Node node = prevNode.next;
-
- while (node != null && c.Compare(prevNode.item, node.item) <= 0)
- {
- prevNode = node;
- node = prevNode.next;
- }
-
- // Completed a run; prevNode is the last node of that run
- prevNode.next = null; // Finish the run
- runTail.prev = node; // Link it into the chain of runs
- runTail = node;
- if (c.Compare(endsentinel.prev.item, prevNode.item) <= 0)
- endsentinel.prev = prevNode; // Update last pointer to point to largest
-
- prevNode = node; // Start a new run
- }
-
- // Repeatedly merge runs two and two, until only one run remains
- while (startsentinel.next.prev != null)
- {
- Node run = startsentinel.next;
- Node newRunTail = null;
-
- while (run != null && run.prev != null)
- { // At least two runs, merge
- Node nextRun = run.prev.prev;
- Node newrun = mergeRuns(run, run.prev, c);
-
- if (newRunTail != null)
- newRunTail.prev = newrun;
- else
- startsentinel.next = newrun;
-
- newRunTail = newrun;
- run = nextRun;
- }
-
- if (run != null) // Add the last run, if any
- newRunTail.prev = run;
- }
-
- endsentinel.prev.next = endsentinel;
- startsentinel.next.prev = startsentinel;
-
- //assert invariant();
- //assert isSorted();
-#if HASHINDEX
- {
- Node cursor = startsentinel.next, end = endsentinel;
- int tag, taglimit;
- TagGroup t = gettaggroup(startsentinel, endsentinel, out tag, out taglimit);
- int tagdelta = taglimit / (size + 1) - tag / (size + 1);
- tagdelta = tagdelta == 0 ? 1 : tagdelta;
- if (underlying == null)
- taggroups = 1;
- while (cursor != end)
- {
- tag = tag + tagdelta > taglimit ? taglimit : tag + tagdelta;
- cursor.tag = tag;
- t.count++;
- cursor.taggroup = t;
- cursor = cursor.next;
- }
- if (t != startsentinel.taggroup)
- t.first = startsentinel.next;
- if (t != endsentinel.taggroup)
- t.last = endsentinel.prev;
- if (tag == taglimit)
- splittaggroup(t);
- }
-#endif
- (underlying ?? this).raiseCollectionChanged();
- }
-
- private static Node mergeRuns(Node run1, Node run2, SCG.IComparer<T> c)
- {
- //assert run1 != null && run2 != null;
- Node prev;
- bool prev1; // is prev from run1?
-
- if (c.Compare(run1.item, run2.item) <= 0)
- {
- prev = run1;
- prev1 = true;
- run1 = run1.next;
- }
- else
- {
- prev = run2;
- prev1 = false;
- run2 = run2.next;
- }
-
- Node start = prev;
-
- //assert start != null;
- start.prev = null;
- while (run1 != null && run2 != null)
- {
- if (prev1)
- {
- //assert prev.next == run1;
- //Comparable run2item = (Comparable)run2.item;
- while (run1 != null && c.Compare(run2.item, run1.item) >= 0)
- {
- prev = run1;
- run1 = prev.next;
- }
-
- if (run1 != null)
- { // prev.item <= run2.item < run1.item; insert run2
- prev.next = run2;
- run2.prev = prev;
- prev = run2;
- run2 = prev.next;
- prev1 = false;
- }
- }
- else
- {
- //assert prev.next == run2;
- //Comparable run1item = (Comparable)run1.item;
- while (run2 != null && c.Compare(run1.item, run2.item) > 0)
- {
- prev = run2;
- run2 = prev.next;
- }
-
- if (run2 != null)
- { // prev.item < run1.item <= run2.item; insert run1
- prev.next = run1;
- run1.prev = prev;
- prev = run1;
- run1 = prev.next;
- prev1 = true;
- }
- }
- }
-
- //assert !(run1 != null && prev1) && !(run2 != null && !prev1);
- if (run1 != null)
- { // last run2 < all of run1; attach run1 at end
- prev.next = run1;
- run1.prev = prev;
- }
- else if (run2 != null)
- { // last run1
- prev.next = run2;
- run2.prev = prev;
- }
-
- return start;
- }
-
- /// <summary>
- /// Randomly shuffle the items of this list.
- /// <para>Will invalidate overlapping views???</para>
- /// </summary>
- public virtual void Shuffle() { Shuffle(new C5Random()); }
-
-
- /// <summary>
- /// Shuffle the items of this list according to a specific random source.
- /// <para>Will invalidate overlapping views???</para>
- /// </summary>
- /// <param name="rnd">The random source.</param>
- public virtual void Shuffle(Random rnd)
- {
- updatecheck();
- if (size == 0)
- return;
- disposeOverlappingViews(false);
- ArrayList<T> a = new ArrayList<T>();
- a.AddAll(this);
- a.Shuffle(rnd);
- Node cursor = startsentinel.next;
- int j = 0;
- while (cursor != endsentinel)
- {
- cursor.item = a[j++];
-#if HASHINDEX
- dict[cursor.item] = cursor;
-#endif
- cursor = cursor.next;
- }
- (underlying ?? this).raiseCollectionChanged();
- }
-
- #endregion
-
- #region IIndexed<T> Members
-
- /// <summary>
- /// <exception cref="IndexOutOfRangeException"/>.
- /// </summary>
- /// <value>The directed collection of items in a specific index interval.</value>
- /// <param name="start">The low index of the interval (inclusive).</param>
- /// <param name="count">The size of the range.</param>
- [Tested]
- public IDirectedCollectionValue<T> this[int start, int count]
- {
- [Tested]
- get
- {
- validitycheck();
- checkRange(start, count);
- return new Range(this, start, count, true);
- }
- }
-
- /// <summary>
- /// Searches for an item in the list going forwrds from the start.
- /// </summary>
- /// <param name="item">Item to search for.</param>
- /// <returns>Index of item from start.</returns>
- [Tested]
- public virtual int IndexOf(T item)
- {
- validitycheck();
- Node node;
-#if HASHINDEX
- if (!dict.Find(item, out node) || !insideview(node))
- return ~size;
-#endif
- node = startsentinel.next;
- int index = 0;
- if (find(item, ref node, ref index))
- return index;
- else
- return ~size;
- }
-
- /// <summary>
- /// Searches for an item in the list going backwords from the end.
- /// </summary>
- /// <param name="item">Item to search for.</param>
- /// <returns>Index of of item from the end.</returns>
- [Tested]
- public virtual int LastIndexOf(T item)
- {
-#if HASHINDEX
- return IndexOf(item);
-#else
- validitycheck();
-
- Node node = endsentinel.prev;
- int index = size - 1;
-
- if (dnif(item, ref node, ref index))
- return index;
- else
- return ~size;
-#endif
- }
-
- /// <summary>
- /// Remove the item at a specific position of the list.
- /// <exception cref="IndexOutOfRangeException"/> if i is negative or
- /// >= the size of the collection.
- /// </summary>
- /// <param name="i">The index of the item to remove.</param>
- /// <returns>The removed item.</returns>
- [Tested]
- public virtual T RemoveAt(int i)
- {
- updatecheck();
- T retval = remove(get(i), i);
-#if HASHINDEX
- dict.Remove(retval);
-#endif
- if (ActiveEvents != EventTypeEnum.None)
- (underlying ?? this).raiseForRemoveAt(Offset + i, retval);
- return retval;
- }
-
- /// <summary>
- /// Remove all items in an index interval.
- /// <exception cref="IndexOutOfRangeException"/>???.
- /// </summary>
- /// <param name="start">The index of the first item to remove.</param>
- /// <param name="count">The number of items to remove.</param>
- [Tested]
- public virtual void RemoveInterval(int start, int count)
- {
-#if HASHINDEX
- updatecheck();
- checkRange(start, count);
- if (count == 0)
- return;
-
- View(start, count).Clear();
-#else
- //Note: this is really almost equaivalent to Clear on a view
- updatecheck();
- checkRange(start, count);
- if (count == 0)
- return;
-
- //for small count: optimize
- //use an optimal get(int i, int j, ref Node ni, ref Node nj)?
- Node a = get(start), b = get(start + count - 1);
- fixViewsBeforeRemove(start, count, a, b);
- a.prev.next = b.next;
- b.next.prev = a.prev;
- if (underlying != null)
- underlying.size -= count;
-
- size -= count;
- if (ActiveEvents != EventTypeEnum.None)
- (underlying ?? this).raiseForRemoveInterval(start + Offset, count);
-#endif
- }
-
- void raiseForRemoveInterval(int start, int count)
- {
- if (ActiveEvents != 0)
- {
- raiseCollectionCleared(size == 0, count, start);
- raiseCollectionChanged();
- }
- }
- #endregion
-
- #region ISequenced<T> Members
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- [Tested]
- public override int GetSequencedHashCode() { validitycheck(); return base.GetSequencedHashCode(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="that"></param>
- /// <returns></returns>
- [Tested]
- public override bool SequencedEquals(ISequenced<T> that) { validitycheck(); return base.SequencedEquals(that); }
-
- #endregion
-
- #region IDirectedCollection<T> Members
-
- /// <summary>
- /// Create a collection containing the same items as this collection, but
- /// whose enumerator will enumerate the items backwards. The new collection
- /// will become invalid if the original is modified. Method typicaly used as in
- /// <code>foreach (T x in coll.Backwards()) {...}</code>
- /// </summary>
- /// <returns>The backwards collection.</returns>
- [Tested]
- public override IDirectedCollectionValue<T> Backwards()
- { return this[0, size].Backwards(); }
-
- #endregion
-
- #region IDirectedEnumerable<T> Members
-
- [Tested]
- IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return Backwards(); }
-
- #endregion
-
- #region IEditableCollection<T> Members
-
- /// <summary>
- /// The value is symbolic indicating the type of asymptotic complexity
- /// in terms of the size of this collection (worst-case or amortized as
- /// relevant).
- /// </summary>
- /// <value>Speed.Linear</value>
- [Tested]
- public virtual Speed ContainsSpeed
- {
- [Tested]
- get
- {
-#if HASHINDEX
- return Speed.Constant;
-#else
- return Speed.Linear;
-#endif
- }
- }
-
- /// <summary>
- /// Performs a check for view validity before calling base.GetUnsequencedHashCode()
- /// </summary>
- /// <returns></returns>
- [Tested]
- public override int GetUnsequencedHashCode()
- { validitycheck(); return base.GetUnsequencedHashCode(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="that"></param>
- /// <returns></returns>
- [Tested]
- public override bool UnsequencedEquals(ICollection<T> that)
- { validitycheck(); return base.UnsequencedEquals(that); }
-
- /// <summary>
- /// Check if this collection contains (an item equivalent to according to the
- /// itemequalityComparer) a particular value.
- /// </summary>
- /// <param name="item">The value to check for.</param>
- /// <returns>True if the items is in this collection.</returns>
- [Tested]
- public virtual bool Contains(T item)
- {
- validitycheck();
- Node node;
- return contains(item, out node);
- }
-
- /// <summary>
- /// Check if this collection contains an item equivalent according to the
- /// itemequalityComparer to a particular value. If so, return in the ref argument (a
- /// binary copy of) the actual value found.
- /// </summary>
- /// <param name="item">The value to look for.</param>
- /// <returns>True if the items is in this collection.</returns>
- [Tested]
- public virtual bool Find(ref T item)
- {
- validitycheck();
- Node node;
- if (contains(item, out node)) { item = node.item; return true; }
- return false;
- }
-
- /// <summary>
- /// Check if this collection contains an item equivalent according to the
- /// itemequalityComparer to a particular value. If so, update the item in the collection
- /// to with a binary copy of the supplied value. Will update a single item.
- /// </summary>
- /// <param name="item">Value to update.</param>
- /// <returns>True if the item was found and hence updated.</returns>
- [Tested]
- public virtual bool Update(T item) { T olditem; return Update(item, out olditem); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <param name="olditem"></param>
- /// <returns></returns>
- public virtual bool Update(T item, out T olditem)
- {
- updatecheck();
- Node node;
-
- if (contains(item, out node))
- {
- olditem = node.item;
- node.item = item;
-#if HASHINDEX
- //Avoid clinging onto a reference to olditem via dict!
- dict.Update(item, node);
-#endif
- (underlying ?? this).raiseForUpdate(item, olditem);
- return true;
- }
-
- olditem = default(T);
- return false;
- }
-
- /// <summary>
- /// Check if this collection contains an item equivalent according to the
- /// itemequalityComparer to a particular value. If so, return in the ref argument (a
- /// binary copy of) the actual value found. Else, add the item to the collection.
- /// </summary>
- /// <param name="item">The value to look for.</param>
- /// <returns>True if the item was found (hence not added).</returns>
- [Tested]
- public virtual bool FindOrAdd(ref T item)
- {
- updatecheck();
-#if HASHINDEX
- //This is an extended myinsert:
- Node node = new Node(item);
- if (!dict.FindOrAdd(item, ref node))
- {
- insertNode(true, endsentinel, node);
- (underlying ?? this).raiseForAdd(item);
- return false;
- }
- if (!insideview(node))
- throw new ArgumentException("Item alredy in indexed list but outside view");
- item = node.item;
- return true;
-#else
- if (Find(ref item))
- return true;
-
- Add(item);
- return false;
-#endif
- }
-
- /// <summary>
- /// Check if this collection contains an item equivalent according to the
- /// itemequalityComparer to a particular value. If so, update the item in the collection
- /// to with a binary copy of the supplied value; else add the value to the collection.
- /// </summary>
- /// <param name="item">Value to add or update.</param>
- /// <returns>True if the item was found and updated (hence not added).</returns>
- [Tested]
- public virtual bool UpdateOrAdd(T item) { T olditem; return UpdateOrAdd(item, out olditem); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <param name="olditem"></param>
- /// <returns></returns>
- public virtual bool UpdateOrAdd(T item, out T olditem)
- {
- updatecheck();
-#if HASHINDEX
- Node node = new Node(item);
- //NOTE: it is hard to do this without double access to the dictionary
- //in the update case
- if (dict.FindOrAdd(item, ref node))
- {
- if (!insideview(node))
- throw new ArgumentException("Item in indexed list but outside view");
- olditem = node.item;
- //Avoid clinging onto a reference to olditem via dict!
- dict.Update(item, node);
- node.item = item;
- (underlying ?? this).raiseForUpdate(item, olditem);
- return true;
- }
- insertNode(true, endsentinel, node);
- (underlying ?? this).raiseForAdd(item);
-#else
- if (Update(item, out olditem))
- return true;
- Add(item);
-#endif
- olditem = default(T);
- return false;
- }
-
- /// <summary>
- /// Remove a particular item from this collection. Since the collection has bag
- /// semantics only one copy equivalent to the supplied item is removed.
- /// </summary>
- /// <param name="item">The value to remove.</param>
- /// <returns>True if the item was found (and removed).</returns>
- [Tested]
- public virtual bool Remove(T item)
- {
- updatecheck();
- int i = 0;
- Node node;
-#if HASHINDEX
- if (!dictremove(item, out node))
-#else
- node = fIFO ? startsentinel.next : endsentinel.prev;
- if (!(fIFO ? find(item, ref node, ref i) : dnif(item, ref node, ref i)))
-#endif
- return false;
- T removeditem = remove(node, i);
- (underlying ?? this).raiseForRemove(removeditem);
- return true;
- }
-
- /// <summary>
- /// Remove a particular item from this collection if found (only one copy).
- /// If an item was removed, report a binary copy of the actual item removed in
- /// the argument.
- /// </summary>
- /// <param name="item">The value to remove on input.</param>
- /// <param name="removeditem">The value removed.</param>
- /// <returns>True if the item was found (and removed).</returns>
- [Tested]
- public virtual bool Remove(T item, out T removeditem)
- {
- updatecheck();
- int i = 0;
- Node node;
-#if HASHINDEX
- if (!dictremove(item, out node))
-#else
- node = fIFO ? startsentinel.next : endsentinel.prev;
- if (!(fIFO ? find(item, ref node, ref i) : dnif(item, ref node, ref i)))
-#endif
- {
- removeditem = default(T);
- return false;
- }
- removeditem = node.item;
- remove(node, i);
- (underlying ?? this).raiseForRemove(removeditem);
- return true;
- }
-
- /// <summary>
- /// Remove all items in another collection from this one, taking multiplicities into account.
- /// <para>Always removes from the front of the list.
- /// </para>
- /// <para>The asymptotic running time complexity of this method is <code>O(n+m+v*log(v))</code>,
- /// where <code>n</code> is the size of this list, <code>m</code> is the size of the
- /// <code>items</code> collection and <code>v</code> is the number of views.
- /// The method will temporarily allocate memory of size <code>O(m+v)</code>.
- /// </para>
- /// </summary>
- /// <typeparam name="U"></typeparam>
- /// <param name="items">The items to remove.</param>
- [Tested]
- public virtual void RemoveAll<U>(SCG.IEnumerable<U> items) where U : T
- {
- updatecheck();
- if (size == 0)
- return;
- RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this);
- bool mustFire = raiseHandler.MustFire;
-#if HASHINDEX
- Node node;
- foreach (T item in items)
- if (dictremove(item, out node))
- {
- if (mustFire)
- raiseHandler.Remove(node.item);
- remove(node, 118);
- }
-#else
- HashBag<T> toremove = new HashBag<T>(itemequalityComparer);
- toremove.AddAll(items);
- ViewHandler viewHandler = new ViewHandler(this);
- int index = 0, removed = 0, myoffset = Offset;
- Node node = startsentinel.next;
- while (node != endsentinel)
- {
- //pass by a stretch of nodes
- while (node != endsentinel && !toremove.Contains(node.item))
- {
- node = node.next;
- index++;
- }
- viewHandler.skipEndpoints(removed, myoffset + index);
- //Remove a stretch of nodes
- Node localend = node.prev; //Latest node not to be removed
- while (node != endsentinel && toremove.Remove(node.item))
- {
- if (mustFire)
- raiseHandler.Remove(node.item);
- removed++;
- node = node.next;
- index++;
- viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
- }
- viewHandler.updateSentinels(myoffset + index, localend, node);
- localend.next = node;
- node.prev = localend;
- }
- index = underlying != null ? underlying.size + 1 - myoffset : size + 1 - myoffset;
- viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
- size -= removed;
- if (underlying != null)
- underlying.size -= removed;
-#endif
- raiseHandler.Raise();
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="predicate"></param>
- void RemoveAll(Fun<T, bool> predicate)
- {
- updatecheck();
- if (size == 0)
- return;
- RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this);
- bool mustFire = raiseHandler.MustFire;
-#if HASHINDEX
- {
- Node n = startsentinel.next;
-
- while (n != endsentinel)
- {
- bool removeIt = predicate(n.item);
- updatecheck();
- if (removeIt)
- {
- dict.Remove(n.item);
- remove(n, 119);
- if (mustFire)
- raiseHandler.Remove(n.item);
- }
-
- n = n.next;
- }
- }
-#else
- ViewHandler viewHandler = new ViewHandler(this);
- int index = 0, removed = 0, myoffset = Offset;
- Node node = startsentinel.next;
- while (node != endsentinel)
- {
- //pass by a stretch of nodes
- while (node != endsentinel && !predicate(node.item))
- {
- updatecheck();
- node = node.next;
- index++;
- }
- updatecheck();
- viewHandler.skipEndpoints(removed, myoffset + index);
- //Remove a stretch of nodes
- Node localend = node.prev; //Latest node not to be removed
- while (node != endsentinel && predicate(node.item))
- {
- updatecheck();
- if (mustFire)
- raiseHandler.Remove(node.item);
- removed++;
- node = node.next;
- index++;
- viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
- }
- updatecheck();
- viewHandler.updateSentinels(myoffset + index, localend, node);
- localend.next = node;
- node.prev = localend;
- }
- index = underlying != null ? underlying.size + 1 - myoffset : size + 1 - myoffset;
- viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
- size -= removed;
- if (underlying != null)
- underlying.size -= removed;
-#endif
- raiseHandler.Raise();
- }
-
- /// <summary>
- /// Remove all items from this collection.
- /// </summary>
- [Tested]
- public virtual void Clear()
- {
- updatecheck();
- if (size == 0)
- return;
- int oldsize = size;
-#if HASHINDEX
- if (underlying == null)
- dict.Clear();
- else
- foreach (T item in this)
- dict.Remove(item);
-#endif
- clear();
- (underlying ?? this).raiseForRemoveInterval(Offset, oldsize);
- }
-
- void clear()
- {
- if (size == 0)
- return;
-#if HASHINDEX
- //TODO: mix with tag maintenance to only run through list once?
- ViewHandler viewHandler = new ViewHandler(this);
- if (viewHandler.viewCount > 0)
- {
- int removed = 0;
- Node n = startsentinel.next;
- viewHandler.skipEndpoints(0, n);
- while (n != endsentinel)
- {
- removed++;
- n = n.next;
- viewHandler.updateViewSizesAndCounts(removed, n);
- }
- viewHandler.updateSentinels(endsentinel, startsentinel, endsentinel);
- if (underlying != null)
- viewHandler.updateViewSizesAndCounts(removed, underlying.endsentinel);
- }
-#else
- fixViewsBeforeRemove(Offset, size, startsentinel.next, endsentinel.prev);
-#endif
-#if HASHINDEX
- if (underlying != null)
- {
- Node n = startsentinel.next;
-
- while (n != endsentinel)
- {
- n.next.prev = startsentinel;
- startsentinel.next = n.next;
- removefromtaggroup(n);
- n = n.next;
- }
- }
- else
- taggroups = 0;
-#endif
- endsentinel.prev = startsentinel;
- startsentinel.next = endsentinel;
- if (underlying != null)
- underlying.size -= size;
- size = 0;
- }
-
- /// <summary>
- /// Remove all items not in some other collection from this one, taking multiplicities into account.
- /// <para>The asymptotic running time complexity of this method is <code>O(n+m+v*log(v))</code>,
- /// where <code>n</code> is the size of this collection, <code>m</code> is the size of the
- /// <code>items</code> collection and <code>v</code> is the number of views.
- /// The method will temporarily allocate memory of size <code>O(m+v)</code>. The stated complexitiy
- /// holds under the assumption that the itemequalityComparer of this list is well-behaved.
- /// </para>
- /// </summary>
- /// <typeparam name="U"></typeparam>
- /// <param name="items">The items to retain.</param>
- [Tested]
- public virtual void RetainAll<U>(SCG.IEnumerable<U> items) where U : T
- {
- updatecheck();
- if (size == 0)
- return;
- RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this);
- bool mustFire = raiseHandler.MustFire;
-#if HASHINDEX
- /*if (underlying == null)
- {
- HashDictionary<T, Node> newdict = new HashDictionary<T, Node>(itemequalityComparer);
- foreach (T item in items)
- {
- Node node;
-
- if (dict.Remove(item, out node))
- newdict.Add(item, node);
- }
- foreach (KeyValuePair<T, Node> pair in dict)
- {
- Node n = pair.Value;
- fixViewsBeforeSingleRemove(n, 117);
- Node p = n.prev, s = n.next; s.prev = p; p.next = s;
- removefromtaggroup(n);
- }
- dict = newdict;
- size = dict.Count;
- //For a small number of items to retain it might be faster to
- //iterate through the list and splice out the chunks not needed
- }
- else*/
- {
- HashSet<T> toremove = new HashSet<T>(itemequalityComparer);
-
- foreach (T item in this)
- toremove.Add(item);
-
- foreach (T item in items)
- toremove.Remove(item);
-
- Node n = startsentinel.next;
-
- while (n != endsentinel && toremove.Count > 0)
- {
- if (toremove.Contains(n.item))
- {
- dict.Remove(n.item);
- remove(n, 119);
- if (mustFire)
- raiseHandler.Remove(n.item);
- }
-
- n = n.next;
- }
- }
-#else
- HashBag<T> toretain = new HashBag<T>(itemequalityComparer);
- toretain.AddAll(items);
- ViewHandler viewHandler = new ViewHandler(this);
- int index = 0, removed = 0, myoffset = Offset;
- Node node = startsentinel.next;
- while (node != endsentinel)
- {
- //Skip a stretch of nodes
- while (node != endsentinel && toretain.Remove(node.item))
- {
- node = node.next;
- index++;
- }
- viewHandler.skipEndpoints(removed, myoffset + index);
- //Remove a stretch of nodes
- Node localend = node.prev; //Latest node not to be removed
- while (node != endsentinel && !toretain.Contains(node.item))
- {
- if (mustFire)
- raiseHandler.Remove(node.item);
- removed++;
- node = node.next;
- index++;
- viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
- }
- viewHandler.updateSentinels(myoffset + index, localend, node);
- localend.next = node;
- node.prev = localend;
- }
- index = underlying != null ? underlying.size + 1 - myoffset : size + 1 - myoffset;
- viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
- size -= removed;
- if (underlying != null)
- underlying.size -= removed;
-#endif
- raiseHandler.Raise();
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="predicate"></param>
- void RetainAll(Fun<T, bool> predicate)
- {
- updatecheck();
- if (size == 0)
- return;
- RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this);
- bool mustFire = raiseHandler.MustFire;
-#if HASHINDEX
- {
- Node n = startsentinel.next;
-
- while (n != endsentinel)
- {
- bool removeIt = !predicate(n.item);
- updatecheck();
- if (removeIt)
- {
- dict.Remove(n.item);
- remove(n, 119);
- if (mustFire)
- raiseHandler.Remove(n.item);
- }
-
- n = n.next;
- }
- }
-#else
- ViewHandler viewHandler = new ViewHandler(this);
- int index = 0, removed = 0, myoffset = Offset;
- Node node = startsentinel.next;
- while (node != endsentinel)
- {
- //Skip a stretch of nodes
- while (node != endsentinel && predicate(node.item))
- {
- updatecheck();
- node = node.next;
- index++;
- }
- updatecheck();
- viewHandler.skipEndpoints(removed, myoffset + index);
- //Remove a stretch of nodes
- Node localend = node.prev; //Latest node not to be removed
- while (node != endsentinel && !predicate(node.item))
- {
- updatecheck();
- if (mustFire)
- raiseHandler.Remove(node.item);
- removed++;
- node = node.next;
- index++;
- viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
- }
- updatecheck();
- viewHandler.updateSentinels(myoffset + index, localend, node);
- localend.next = node;
- node.prev = localend;
- }
- index = underlying != null ? underlying.size + 1 - myoffset : size + 1 - myoffset;
- viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
- size -= removed;
- if (underlying != null)
- underlying.size -= removed;
-#endif
- raiseHandler.Raise();
- }
-
- /// <summary>
- /// Check if this collection contains all the values in another collection
- /// with respect to multiplicities.
- /// </summary>
- /// <param name="items">The </param>
- /// <typeparam name="U"></typeparam>
- /// <returns>True if all values in <code>items</code>is in this collection.</returns>
- [Tested]
- public virtual bool ContainsAll<U>(SCG.IEnumerable<U> items) where U : T
- {
- validitycheck();
-#if HASHINDEX
- Node node;
- foreach (T item in items)
- if (!contains(item, out node))
- return false;
- return true;
-#else
- HashBag<T> tocheck = new HashBag<T>(itemequalityComparer);
- tocheck.AddAll(items);
- if (tocheck.Count > size)
- return false;
- Node node = startsentinel.next;
- while (node != endsentinel)
- {
- tocheck.Remove(node.item);
- node = node.next;
- }
- return tocheck.IsEmpty;
-#endif
- }
-
-
- /// <summary>
- /// Create a new list consisting of the items of this list satisfying a
- /// certain predicate.
- /// </summary>
- /// <param name="filter">The filter delegate defining the predicate.</param>
- /// <returns>The new list.</returns>
- [Tested]
- public IList<T> FindAll(Fun<T, bool> filter)
- {
- validitycheck();
- int stamp = this.stamp;
- HashedLinkedList<T> retval = new HashedLinkedList<T>();
- Node cursor = startsentinel.next;
- Node mcursor = retval.startsentinel;
-#if HASHINDEX
- double tagdelta = int.MaxValue / (size + 1.0);
- int count = 1;
- TagGroup taggroup = new TagGroup();
- retval.taggroups = 1;
-#endif
- while (cursor != endsentinel)
- {
- bool found = filter(cursor.item);
- modifycheck(stamp);
- if (found)
- {
- mcursor.next = new Node(cursor.item, mcursor, null);
- mcursor = mcursor.next;
- retval.size++;
-#if HASHINDEX
- retval.dict.Add(cursor.item, mcursor);
- mcursor.taggroup = taggroup;
- mcursor.tag = (int)(tagdelta * count++);
-#endif
- }
- cursor = cursor.next;
- }
-#if HASHINDEX
- if (retval.size > 0)
- {
- taggroup.count = retval.size;
- taggroup.first = retval.startsentinel.next;
- taggroup.last = mcursor;
- }
-#endif
- retval.endsentinel.prev = mcursor;
- mcursor.next = retval.endsentinel;
- return retval;
- }
-
-
- /// <summary>
- /// Count the number of items of the collection equal to a particular value.
- /// Returns 0 if and only if the value is not in the collection.
- /// </summary>
- /// <param name="item">The value to count.</param>
- /// <returns>The number of copies found.</returns>
- [Tested]
- public virtual int ContainsCount(T item)
- {
-#if HASHINDEX
- return Contains(item) ? 1 : 0;
-#else
- validitycheck();
- int retval = 0;
- Node node = startsentinel.next;
- while (node != endsentinel)
- {
- if (itemequalityComparer.Equals(node.item, item))
- retval++;
- node = node.next;
- }
- return retval;
-#endif
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public virtual ICollectionValue<T> UniqueItems()
- {
-#if HASHINDEX
- return this;
-#else
- HashBag<T> hashbag = new HashBag<T>(itemequalityComparer);
- hashbag.AddAll(this);
- return hashbag.UniqueItems();
-#endif
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public virtual ICollectionValue<KeyValuePair<T, int>> ItemMultiplicities()
- {
-#if HASHINDEX
- return new MultiplicityOne<T>(this);
-#else
- HashBag<T> hashbag = new HashBag<T>(itemequalityComparer);
- hashbag.AddAll(this);
- return hashbag.ItemMultiplicities();
-#endif
- }
-
- /// <summary>
- /// Remove all items equivalent to a given value.
- /// <para>The asymptotic complexity of this method is <code>O(n+v*log(v))</code>,
- /// where <code>n</code> is the size of the collection and <code>v</code>
- /// is the number of views.
- /// </para>
- /// </summary>
- /// <param name="item">The value to remove.</param>
- [Tested]
- public virtual void RemoveAllCopies(T item)
- {
-#if HASHINDEX
- Remove(item);
-#else
- updatecheck();
- if (size == 0)
- return;
- RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this);
- bool mustFire = raiseHandler.MustFire;
- ViewHandler viewHandler = new ViewHandler(this);
- int index = 0, removed = 0, myoffset = Offset;
- //
- Node node = startsentinel.next;
- while (node != endsentinel)
- {
- //pass by a stretch of nodes
- while (node != endsentinel && !itemequalityComparer.Equals(node.item, item))
- {
- node = node.next;
- index++;
- }
- viewHandler.skipEndpoints(removed, myoffset + index);
- //Remove a stretch of nodes
- Node localend = node.prev; //Latest node not to be removed
- while (node != endsentinel && itemequalityComparer.Equals(node.item, item))
- {
- if (mustFire)
- raiseHandler.Remove(node.item);
- removed++;
- node = node.next;
- index++;
- viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
- }
- viewHandler.updateSentinels(myoffset + index, localend, node);
- localend.next = node;
- node.prev = localend;
- }
- index = underlying != null ? underlying.size + 1 - myoffset : size + 1 - myoffset;
- viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
- size -= removed;
- if (underlying != null)
- underlying.size -= removed;
- raiseHandler.Raise();
-#endif
- }
-
- #endregion
-
- #region ICollectionValue<T> Members
-
- /// <summary>
- ///
- /// </summary>
- /// <value>The number of items in this collection</value>
- [Tested]
- public override int Count { [Tested]get { validitycheck(); return size; } }
-
- /// <summary>
- /// Choose some item of this collection.
- /// </summary>
- /// <exception cref="NoSuchItemException">if collection is empty.</exception>
- /// <returns></returns>
- [Tested]
- public override T Choose() { return First; }
-
- /// <summary>
- /// Create an enumerable, enumerating the items of this collection that satisfies
- /// a certain condition.
- /// </summary>
- /// <param name="filter">The T->bool filter delegate defining the condition</param>
- /// <returns>The filtered enumerable</returns>
- public override SCG.IEnumerable<T> Filter(Fun<T, bool> filter) { validitycheck(); return base.Filter(filter); }
-
- #endregion
-
- #region IEnumerable<T> Members
- /// <summary>
- /// Create an enumerator for the collection
- /// </summary>
- /// <returns>The enumerator</returns>
- [Tested]
- public override SCG.IEnumerator<T> GetEnumerator()
- {
- validitycheck();
- Node cursor = startsentinel.next;
- int enumeratorstamp = underlying != null ? underlying.stamp : this.stamp;
-
- while (cursor != endsentinel)
- {
- modifycheck(enumeratorstamp);
- yield return cursor.item;
- cursor = cursor.next;
- }
- }
-
- #endregion
-
- #region IExtensible<T> Members
- /// <summary>
- /// Add an item to this collection if possible.
- /// </summary>
- /// <param name="item">The item to add.</param>
- /// <returns>True.</returns>
- [Tested]
- public virtual bool Add(T item)
- {
- updatecheck();
-#if HASHINDEX
- Node node = new Node(item);
- if (!dict.FindOrAdd(item, ref node))
- {
- insertNode(true, endsentinel, node);
- (underlying ?? this).raiseForAdd(item);
- return true;
- }
- return false;
-#else
- insert(size, endsentinel, item);
- (underlying ?? this).raiseForAdd(item);
- return true;
-#endif
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <value>True since this collection has bag semantics.</value>
- [Tested]
- public virtual bool AllowsDuplicates
- {
- [Tested]
- get
- {
-#if HASHINDEX
- return false;
-#else
- return true;
-#endif
- }
- }
-
- /// <summary>
- /// By convention this is true for any collection with set semantics.
- /// </summary>
- /// <value>True if only one representative of a group of equal items
- /// is kept in the collection together with the total count.</value>
- public virtual bool DuplicatesByCounting
- {
- get
- {
-#if HASHINDEX
- return true;
-#else
- return false;
-#endif
- }
- }
-
- /// <summary>
- /// Add the elements from another collection with a more specialized item type
- /// to this collection.
- /// </summary>
- /// <typeparam name="U">The type of items to add</typeparam>
- /// <param name="items">The items to add</param>
- [Tested]
- public virtual void AddAll<U>(SCG.IEnumerable<U> items) where U : T
- {
-#if HASHINDEX
- updatecheck();
- int added = 0;
- Node pred = endsentinel.prev;
- foreach (U item in items)
- {
- Node node = new Node(item);
- if (!dict.FindOrAdd(item, ref node))
- {
- insertNode(false, endsentinel, node);
- added++;
- }
- }
- if (added > 0)
- {
- fixViewsAfterInsert(endsentinel, pred, added, 0);
- raiseForInsertAll(pred, size - added, added, false);
- }
-#else
- insertAll(size, items, false);
-#endif
- }
-
- #endregion
-
-#if HASHINDEX
-#else
- #region IStack<T> Members
-
- /// <summary>
- /// Push an item to the top of the stack.
- /// </summary>
- /// <param name="item">The item</param>
- [Tested]
- public void Push(T item)
- {
- InsertLast(item);
- }
-
- /// <summary>
- /// Pop the item at the top of the stack from the stack.
- /// </summary>
- /// <returns>The popped item.</returns>
- [Tested]
- public T Pop()
- {
- return RemoveLast();
- }
-
- #endregion
-
- #region IQueue<T> Members
-
- /// <summary>
- /// Enqueue an item at the back of the queue.
- /// </summary>
- /// <param name="item">The item</param>
- [Tested]
- public virtual void Enqueue(T item)
- {
- InsertLast(item);
- }
-
- /// <summary>
- /// Dequeue an item from the front of the queue.
- /// </summary>
- /// <returns>The item</returns>
- [Tested]
- public virtual T Dequeue()
- {
- return RemoveFirst();
- }
- #endregion
-#endif
-
- #region Diagnostic
-
- private bool checkViews()
- {
- if (underlying != null)
- throw new InternalException(System.Reflection.MethodInfo.GetCurrentMethod() + " called on a view");
- if (views == null)
- return true;
- bool retval = true;
-
- Node[] nodes = new Node[size + 2];
- int i = 0;
- Node n = startsentinel;
- while (n != null)
- {
- nodes[i++] = n;
- n = n.next;
- }
- //Console.WriteLine("###");
- foreach (HashedLinkedList<T> view in views)
- {
- if (!view.isValid)
- {
- Console.WriteLine("Invalid view(hash {0}, offset {1}, size {2})",
- view.GetHashCode(), view.offset, view.size);
- retval = false;
- continue;
- }
- if (view.Offset > size || view.Offset < 0)
- {
- Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), Offset > underlying.size ({2})",
- view.GetHashCode(), view.offset, view.size, size);
- retval = false;
- }
- else if (view.startsentinel != nodes[view.Offset])
- {
- Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), startsentinel {3} should be {4}",
- view.GetHashCode(), view.offset, view.size,
- view.startsentinel + " " + view.startsentinel.GetHashCode(),
- nodes[view.Offset] + " " + nodes[view.Offset].GetHashCode());
- retval = false;
- }
- if (view.Offset + view.size > size || view.Offset + view.size < 0)
- {
- Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), end index > underlying.size ({3})",
- view.GetHashCode(), view.offset, view.size, size);
- retval = false;
- }
- else if (view.endsentinel != nodes[view.Offset + view.size + 1])
- {
- Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), endsentinel {3} should be {4}",
- view.GetHashCode(), view.offset, view.size,
- view.endsentinel + " " + view.endsentinel.GetHashCode(),
- nodes[view.Offset + view.size + 1] + " " + nodes[view.Offset + view.size + 1].GetHashCode());
- retval = false;
- }
- if (view.views != views)
- {
- Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), wrong views list {3} <> {4}",
- view.GetHashCode(), view.offset, view.size, view.views.GetHashCode(), views.GetHashCode());
- retval = false;
- }
- if (view.underlying != this)
- {
- Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), wrong underlying {3} <> this {4}",
- view.GetHashCode(), view.offset, view.size, view.underlying.GetHashCode(), GetHashCode());
- retval = false;
- }
- if (view.stamp != stamp)
- {
- //Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), wrong stamp view:{2} underlying: {3}", view.GetHashCode(),view.offset, view.size, view.stamp, stamp);
- //retval = false;
- }
- }
- return retval;
- }
-
- string zeitem(Node node)
- {
- return node == null ? "(null node)" : node.item.ToString();
- }
-
- /// <summary>
- /// Check the sanity of this list
- /// </summary>
- /// <returns>true if sane</returns>
- [Tested]
- public virtual bool Check()
- {
- bool retval = true;
-
- /*if (underlying != null && underlying.stamp != stamp)
- {
- Console.WriteLine("underlying != null && underlying.stamp({0}) != stamp({1})", underlying.stamp, stamp);
- retval = false;
- }*/
-
- if (underlying != null)
- {
- //TODO: check that this view is included in viewsEndpoints tree
- return underlying.Check();
- }
-
- if (startsentinel == null)
- {
- Console.WriteLine("startsentinel == null");
- retval = false;
- }
-
- if (endsentinel == null)
- {
- Console.WriteLine("endsentinel == null");
- retval = false;
- }
-
- if (size == 0)
- {
- if (startsentinel != null && startsentinel.next != endsentinel)
- {
- Console.WriteLine("size == 0 but startsentinel.next != endsentinel");
- retval = false;
- }
-
- if (endsentinel != null && endsentinel.prev != startsentinel)
- {
- Console.WriteLine("size == 0 but endsentinel.prev != startsentinel");
- retval = false;
- }
- }
-
- if (startsentinel == null)
- {
- Console.WriteLine("NULL startsentinel");
- return retval;
- }
-
- int count = 0;
- Node node = startsentinel.next, prev = startsentinel;
-#if HASHINDEX
- int taggroupsize = 0, oldtaggroupsize = losize + 1, seentaggroups = 0;
- TagGroup oldtg = null;
-
- if (underlying == null)
- {
- TagGroup tg = startsentinel.taggroup;
-
- if (tg.count != 0 || tg.first != null || tg.last != null || tg.tag != int.MinValue)
- {
- Console.WriteLine("Bad startsentinel tag group: {0}", tg);
- retval = false;
- }
-
- tg = endsentinel.taggroup;
- if (tg.count != 0 || tg.first != null || tg.last != null || tg.tag != int.MaxValue)
- {
- Console.WriteLine("Bad endsentinel tag group: {0}", tg);
- retval = false;
- }
- }
-#endif
- while (node != endsentinel)
- {
- count++;
- if (node.prev != prev)
- {
- Console.WriteLine("Bad backpointer at node {0}", count);
- retval = false;
- }
-#if HASHINDEX
- if (underlying == null)
- {
- if (!node.prev.precedes(node))
- {
- Console.WriteLine("node.prev.tag ({0}, {1}) >= node.tag ({2}, {3}) at index={4} item={5} ", node.prev.taggroup.tag, node.prev.tag, node.taggroup.tag, node.tag, count, node.item);
- retval = false;
- }
-
- if (node.taggroup != oldtg)
- {
-
- if (node.taggroup.first != node)
- {
- string ntfi = zeitem(node.taggroup.first);
- Console.WriteLine("Bad first pointer in taggroup: node.taggroup.first.item ({0}), node.item ({1}) at index={2} item={3}", ntfi, node.item, count, node.item);
- retval = false;
- }
-
- if (oldtg != null)
- {
- if (oldtg.count != taggroupsize)
- {
- Console.WriteLine("Bad taggroupsize: oldtg.count ({0}) != taggroupsize ({1}) at index={2} item={3}", oldtg.count, taggroupsize, count, node.item);
- retval = false;
- }
-
- if (oldtaggroupsize <= losize && taggroupsize <= losize)
- {
- Console.WriteLine("Two small taggroups in a row: oldtaggroupsize ({0}), taggroupsize ({1}) at index={2} item={3}", oldtaggroupsize, taggroupsize, count, node.item);
- retval = false;
- }
-
- if (node.taggroup.tag <= oldtg.tag)
- {
- Console.WriteLine("Taggroup tags not strictly increasing: oldtaggrouptag ({0}), taggrouptag ({1}) at index={2} item={3}", oldtg.tag, node.taggroup.tag, count, node.item);
- retval = false;
- }
-
- if (oldtg.last != node.prev)
- {
- Console.WriteLine("Bad last pointer in taggroup: oldtg.last.item ({0}), node.prev.item ({1}) at index={2} item={3}", oldtg.last.item, node.prev.item, count, node.item);
- retval = false;
- }
-
- oldtaggroupsize = taggroupsize;
- }
-
- seentaggroups++;
- oldtg = node.taggroup;
- taggroupsize = 1;
- }
- else
- {
- taggroupsize++;
- }
- }
-
-#endif
- prev = node;
- node = node.next;
- if (node == null)
- {
- Console.WriteLine("Null next pointer at node {0}", count);
- return false;
- }
- }
-
-#if HASHINDEX
- if (underlying == null && size == 0 && taggroups != 0)
- {
- Console.WriteLine("Bad taggroups for empty list: size={0} taggroups={1}", size, taggroups);
- retval = false;
- }
- if (underlying == null && size > 0)
- {
- oldtg = node.prev.taggroup;
- if (oldtg != null)
- {
- if (oldtg.count != taggroupsize)
- {
- Console.WriteLine("Bad taggroupsize: oldtg.count ({0}) != taggroupsize ({1}) at index={2} item={3}", oldtg.count, taggroupsize, count, node.item);
- retval = false;
- }
-
- if (oldtaggroupsize <= losize && taggroupsize <= losize)
- {
- Console.WriteLine("Two small taggroups in a row: oldtaggroupsize ({0}), taggroupsize ({1}) at index={2} item={3}", oldtaggroupsize, taggroupsize, count, node.item);
- retval = false;
- }
-
- if (node.taggroup.tag <= oldtg.tag)
- {
- Console.WriteLine("Taggroup tags not strictly increasing: oldtaggrouptag ({0}), taggrouptag ({1}) at index={2} item={3}", oldtg.tag, node.taggroup.tag, count, node.item);
- retval = false;
- }
-
- if (oldtg.last != node.prev)
- {
- Console.WriteLine("Bad last pointer in taggroup: oldtg.last.item ({0}), node.prev.item ({1}) at index={2} item={3}", zeitem(oldtg.last), zeitem(node.prev), count, node.item);
- retval = false;
- }
- }
-
- if (seentaggroups != taggroups)
- {
- Console.WriteLine("seentaggroups ({0}) != taggroups ({1}) (at size {2})", seentaggroups, taggroups, size);
- retval = false;
- }
- }
-#endif
- if (count != size)
- {
- Console.WriteLine("size={0} but enumeration gives {1} nodes ", size, count);
- retval = false;
- }
-
- retval = checkViews() && retval;
-
-#if HASHINDEX
- if (!retval)
- return false;
- if (underlying == null)
- {
- if (size != dict.Count)
- {
- Console.WriteLine("list.size ({0}) != dict.Count ({1})", size, dict.Count);
- retval = false;
- }
- Node n = startsentinel.next, n2;
- while (n != endsentinel)
- {
- if (!dict.Find(n.item, out n2))
- {
- Console.WriteLine("Item in list but not dict: {0}", n.item);
- retval = false;
- }
- else if (n != n2)
- {
- Console.WriteLine("Wrong node in dict for item: {0}", n.item);
- retval = false;
- }
- n = n.next;
- }
- }
-#endif
- return retval;
- }
- #endregion
-
- #region ICloneable Members
-
- /// <summary>
- /// Make a shallow copy of this HashedLinkedList.
- /// </summary>
- /// <returns></returns>
- public virtual object Clone()
- {
- HashedLinkedList<T> clone = new HashedLinkedList<T>(itemequalityComparer);
- clone.AddAll(this);
- return clone;
- }
-
- #endregion
-
- #region System.Collections.Generic.IList<T> Members
-
- void System.Collections.Generic.IList<T>.RemoveAt(int index)
- {
- RemoveAt(index);
- }
-
- void System.Collections.Generic.ICollection<T>.Add(T item)
- {
- Add(item);
- }
-
- #endregion
-
- #region System.Collections.ICollection Members
-
- bool System.Collections.ICollection.IsSynchronized
- {
- get { return false; }
- }
-
- [Obsolete]
- Object System.Collections.ICollection.SyncRoot
- {
- // Presumably safe to use the startsentinel (of type Node, always != null) as SyncRoot
- // since the class Node is private.
- get { return underlying != null ? ((System.Collections.ICollection)underlying).SyncRoot : startsentinel; }
- }
-
- void System.Collections.ICollection.CopyTo(Array arr, int index)
- {
- if (index < 0 || index + Count > arr.Length)
- throw new ArgumentOutOfRangeException();
-
- foreach (T item in this)
- arr.SetValue(item, index++);
- }
-
- #endregion
-
- #region System.Collections.IList Members
-
- Object System.Collections.IList.this[int index]
- {
- get { return this[index]; }
- set { this[index] = (T)value; }
- }
-
- int System.Collections.IList.Add(Object o)
- {
- bool added = Add((T)o);
- // What position to report if item not added? SC.IList.Add doesn't say
- return added ? Count-1 : -1;
- }
-
- bool System.Collections.IList.Contains(Object o)
- {
- return Contains((T)o);
- }
-
- int System.Collections.IList.IndexOf(Object o)
- {
- return Math.Max(-1, IndexOf((T)o));
- }
-
- void System.Collections.IList.Insert(int index, Object o)
- {
- Insert(index, (T)o);
- }
-
- void System.Collections.IList.Remove(Object o)
- {
- Remove((T)o);
- }
-
- void System.Collections.IList.RemoveAt(int index)
- {
- RemoveAt(index);
- }
-
- #endregion
- }
-}
+/* + Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#define HASHINDEX + +using System; +using System.Diagnostics; +using SCG = System.Collections.Generic; + +namespace C5 +{ + /// <summary> + /// A list collection class based on a doubly linked list data structure. + /// </summary> + [Serializable] + public class HashedLinkedList<T> : SequencedBase<T>, IList<T>, SCG.IList<T> +#if HASHINDEX +#else +, IStack<T>, IQueue<T> +#endif + { + #region Fields + /// <summary> + /// IExtensible.Add(T) always does AddLast(T), fIFO determines + /// if T Remove() does RemoveFirst() or RemoveLast() + /// </summary> + bool fIFO = true; + + #region Events + + /// <summary> + /// + /// </summary> + /// <value></value> + public override EventTypeEnum ListenableEvents { get { return underlying == null ? EventTypeEnum.All : EventTypeEnum.None; } } + + #endregion + + //Invariant: startsentinel != null && endsentinel != null + //If size==0: startsentinel.next == endsentinel && endsentinel.prev == startsentinel + //Else: startsentinel.next == First && endsentinel.prev == Last) + /// <summary> + /// Node to the left of first node + /// </summary> + Node startsentinel; + /// <summary> + /// Node to the right of last node + /// </summary> + Node endsentinel; + /// <summary> + /// Offset of this view in underlying list + /// </summary> +#if HASHINDEX + int? offset; +#else + int offset; +#endif + + /// <summary> + /// underlying list of this view (or null for the underlying list) + /// </summary> + HashedLinkedList<T> underlying; + + //Note: all views will have the same views list since all view objects are created by MemberwiseClone() + WeakViewList<HashedLinkedList<T>> views; + WeakViewList<HashedLinkedList<T>>.Node myWeakReference; + + /// <summary> + /// Has this list or view not been invalidated by some operation (by someone calling Dispose()) + /// </summary> + bool isValid = true; + + +#if HASHINDEX + HashDictionary<T, Node> dict; + /// <summary> + /// Number of taggroups + /// </summary> + int taggroups; + /// <summary> + /// + /// </summary> + /// <value></value> + int Taggroups + { + get { return underlying == null ? taggroups : underlying.taggroups; } + set { if (underlying == null) taggroups = value; else underlying.taggroups = value; } + } +#endif + + #endregion + + #region Util + + bool equals(T i1, T i2) { return itemequalityComparer.Equals(i1, i2); } + + #region Check utilities + /// <summary> + /// Check if it is valid to perform updates and increment stamp of + /// underlying if this is a view. + /// <para>This method should be called in every public modifying + /// methods before any modifications are performed. + /// </para> + /// </summary> + /// <exception cref="InvalidOperationException"> if check fails.</exception> + protected override void updatecheck() + { + validitycheck(); + base.updatecheck(); + if (underlying != null) + underlying.stamp++; + } + + /// <summary> + /// Check if we are a view that the underlyinglist has only been updated through us. + /// <br/> + /// This method should be called from enumerators etc to guard against + /// modification of the base collection. + /// </summary> + /// <exception cref="InvalidOperationException"> if check fails.</exception> + void validitycheck() + { + if (!isValid) + throw new ViewDisposedException(); + } + + /// <summary> + /// Check that the list has not been updated since a particular time. + /// </summary> + /// <param name="stamp">The stamp indicating the time.</param> + /// <exception cref="CollectionModifiedException"> if check fails.</exception> + protected override void modifycheck(int stamp) + { + validitycheck(); + if ((underlying != null ? underlying.stamp : this.stamp) != stamp) + throw new CollectionModifiedException(); + } + #endregion + + #region Searching + bool contains(T item, out Node node) + { +#if HASHINDEX + if (dict.Find(item, out node)) + return insideview(node); +#else + //TODO: search from both ends? Or search from the end selected by FIFO? + node = startsentinel.next; + while (node != endsentinel) + { + if (equals(item, node.item)) + return true; + node = node.next; + } +#endif + return false; + } + + /// <summary> + /// Search forwards from a node for a node with a particular item. + /// </summary> + /// <param name="item">The item to look for</param> + /// <param name="node">On input, the node to start at. If item was found, the node found on output.</param> + /// <param name="index">If node was found, the value will be the number of links followed higher than + /// the value on input. If item was not found, the value on output is undefined.</param> + /// <returns>True if node was found.</returns> + bool find(T item, ref Node node, ref int index) + { + while (node != endsentinel) + { + //if (item.Equals(node.item)) + if (itemequalityComparer.Equals(item, node.item)) + return true; + + index++; + node = node.next; + } + + return false; + } + + bool dnif(T item, ref Node node, ref int index) + { + while (node != startsentinel) + { + //if (item.Equals(node.item)) + if (itemequalityComparer.Equals(item, node.item)) + return true; + + index--; + node = node.prev; + } + + return false; + } + +#if HASHINDEX + bool insideview(Node node) + { + if (underlying == null) + return true; + return (startsentinel.precedes(node) && node.precedes(endsentinel)); + } +#endif + + #endregion + + #region Indexing + /// <summary> + /// Return the node at position pos + /// </summary> + /// <param name="pos"></param> + /// <returns></returns> + Node get(int pos) + { + if (pos < 0 || pos >= size) + throw new IndexOutOfRangeException(); + else if (pos < size / 2) + { // Closer to front + Node node = startsentinel; + + for (int i = 0; i <= pos; i++) + node = node.next; + + return node; + } + else + { // Closer to end + Node node = endsentinel; + + for (int i = size; i > pos; i--) + node = node.prev; + + return node; + } + } + + /// <summary> + /// Find the distance from pos to the set given by positions. Return the + /// signed distance as return value and as an out parameter, the + /// array index of the nearest position. This is used for up to length 5 of + /// positions, and we do not assume it is sorted. + /// </summary> + /// <param name="pos"></param> + /// <param name="positions"></param> + /// <param name="nearest"></param> + /// <returns></returns> + int dist(int pos, out int nearest, int[] positions) + { + nearest = -1; + int bestdist = int.MaxValue; + int signeddist = bestdist; + for (int i = 0; i < positions.Length; i++) + { + int thisdist = positions[i] - pos; + if (thisdist >= 0 && thisdist < bestdist) { nearest = i; bestdist = thisdist; signeddist = thisdist; } + if (thisdist < 0 && -thisdist < bestdist) { nearest = i; bestdist = -thisdist; signeddist = thisdist; } + } + return signeddist; + } + + /// <summary> + /// Find the node at position pos, given known positions of several nodes. + /// </summary> + /// <param name="pos"></param> + /// <param name="positions"></param> + /// <param name="nodes"></param> + /// <returns></returns> + Node get(int pos, int[] positions, Node[] nodes) + { + int nearest; + int delta = dist(pos, out nearest, positions); + Node node = nodes[nearest]; + if (delta > 0) + for (int i = 0; i < delta; i++) + node = node.prev; + else + for (int i = 0; i > delta; i--) + node = node.next; + return node; + } + + /// <summary> + /// Get nodes at positions p1 and p2, given nodes at several positions. + /// </summary> + /// <param name="p1"></param> + /// <param name="p2"></param> + /// <param name="n1"></param> + /// <param name="n2"></param> + /// <param name="positions"></param> + /// <param name="nodes"></param> + void getPair(int p1, int p2, out Node n1, out Node n2, int[] positions, Node[] nodes) + { + int nearest1, nearest2; + int delta1 = dist(p1, out nearest1, positions), d1 = delta1 < 0 ? -delta1 : delta1; + int delta2 = dist(p2, out nearest2, positions), d2 = delta2 < 0 ? -delta2 : delta2; + + if (d1 < d2) + { + n1 = get(p1, positions, nodes); + n2 = get(p2, new int[] { positions[nearest2], p1 }, new Node[] { nodes[nearest2], n1 }); + } + else + { + n2 = get(p2, positions, nodes); + n1 = get(p1, new int[] { positions[nearest1], p2 }, new Node[] { nodes[nearest1], n2 }); + } + } + #endregion + + #region Insertion +#if HASHINDEX + void insert(int index, Node succ, T item) + { + Node newnode = new Node(item); + if (dict.FindOrAdd(item, ref newnode)) + throw new DuplicateNotAllowedException("Item already in indexed list"); + insertNode(true, succ, newnode); + } + + /// <summary> + /// Insert a Node before another one. Unchecked version. + /// </summary> + /// <param name="succ">The successor to be</param> + /// <param name="newnode">Node to insert</param> + /// <param name="updateViews">update overlapping view in this call</param> + void insertNode(bool updateViews, Node succ, Node newnode) + { + newnode.next = succ; + Node pred = newnode.prev = succ.prev; + succ.prev.next = newnode; + succ.prev = newnode; + size++; + if (underlying != null) + underlying.size++; + settag(newnode); + if (updateViews) + fixViewsAfterInsert(succ, pred, 1, 0); + } +#else + /// <summary> + /// + /// </summary> + /// <param name="index">The index in this view</param> + /// <param name="succ"></param> + /// <param name="item"></param> + /// <returns></returns> + Node insert(int index, Node succ, T item) + { + Node newnode = new Node(item, succ.prev, succ); + succ.prev.next = newnode; + succ.prev = newnode; + size++; + if (underlying != null) + underlying.size++; + fixViewsAfterInsert(succ, newnode.prev, 1, Offset + index); + return newnode; + } +#endif + #endregion + + #region Removal + T remove(Node node, int index) + { + fixViewsBeforeSingleRemove(node, Offset + index); + node.prev.next = node.next; + node.next.prev = node.prev; + size--; + if (underlying != null) + underlying.size--; +#if HASHINDEX + removefromtaggroup(node); +#endif + return node.item; + } + +#if HASHINDEX + private bool dictremove(T item, out Node node) + { + if (underlying == null) + { + if (!dict.Remove(item, out node)) + return false; + } + else + { + //We cannot avoid calling dict twice - have to intersperse the listorder test! + if (!contains(item, out node)) + return false; + dict.Remove(item); + } + return true; + } +#endif + #endregion + + #region fixView utilities + /// <summary> + /// + /// </summary> + /// <param name="added">The actual number of inserted nodes</param> + /// <param name="pred">The predecessor of the inserted nodes</param> + /// <param name="succ">The successor of the added nodes</param> + /// <param name="realInsertionIndex"></param> + void fixViewsAfterInsert(Node succ, Node pred, int added, int realInsertionIndex) + { + if (views != null) + foreach (HashedLinkedList<T> view in views) + { + if (view != this) + { +#if HASHINDEX + if (pred.precedes(view.startsentinel) || (view.startsentinel == pred && view.size > 0)) + view.offset += added; + if (view.startsentinel.precedes(pred) && succ.precedes(view.endsentinel)) + view.size += added; + if (view.startsentinel == pred && view.size > 0) + view.startsentinel = succ.prev; + if (view.endsentinel == succ) + view.endsentinel = pred.next; +#else + if (view.Offset == realInsertionIndex && view.size > 0) + view.startsentinel = succ.prev; + if (view.Offset + view.size == realInsertionIndex) + view.endsentinel = pred.next; + if (view.Offset < realInsertionIndex && view.Offset + view.size > realInsertionIndex) + view.size += added; + if (view.Offset > realInsertionIndex || (view.Offset == realInsertionIndex && view.size > 0)) + view.offset += added; +#endif + } + } + } + + void fixViewsBeforeSingleRemove(Node node, int realRemovalIndex) + { + if (views != null) + foreach (HashedLinkedList<T> view in views) + { + if (view != this) + { +#if HASHINDEX + if (view.startsentinel.precedes(node) && node.precedes(view.endsentinel)) + view.size--; + if (!view.startsentinel.precedes(node)) + view.offset--; + if (view.startsentinel == node) + view.startsentinel = node.prev; + if (view.endsentinel == node) + view.endsentinel = node.next; +#else + if (view.offset - 1 == realRemovalIndex) + view.startsentinel = node.prev; + if (view.offset + view.size == realRemovalIndex) + view.endsentinel = node.next; + if (view.offset <= realRemovalIndex && view.offset + view.size > realRemovalIndex) + view.size--; + if (view.offset > realRemovalIndex) + view.offset--; +#endif + } + } + } + +#if HASHINDEX +#else + void fixViewsBeforeRemove(int start, int count, Node first, Node last) + { + int clearend = start + count - 1; + if (views != null) + foreach (HashedLinkedList<T> view in views) + { + if (view == this) + continue; + int viewoffset = view.Offset, viewend = viewoffset + view.size - 1; + //sentinels + if (start < viewoffset && viewoffset - 1 <= clearend) + view.startsentinel = first.prev; + if (start <= viewend + 1 && viewend < clearend) + view.endsentinel = last.next; + //offsets and sizes + if (start < viewoffset) + { + if (clearend < viewoffset) + view.offset = viewoffset - count; + else + { + view.offset = start; + view.size = clearend < viewend ? viewend - clearend : 0; + } + } + else if (start <= viewend) + view.size = clearend <= viewend ? view.size - count : start - viewoffset; + } + } +#endif + + /// <summary> + /// + /// </summary> + /// <param name="otherView"></param> + /// <returns>The position of View(otherOffset, otherSize) wrt. this view</returns> + MutualViewPosition viewPosition(HashedLinkedList<T> otherView) + { +#if HASHINDEX + Node otherstartsentinel = otherView.startsentinel, otherendsentinel = otherView.endsentinel, + first = startsentinel.next, last = endsentinel.prev, + otherfirst = otherstartsentinel.next, otherlast = otherendsentinel.prev; + if (last.precedes(otherfirst) || otherlast.precedes(first)) + return MutualViewPosition.NonOverlapping; + if (size == 0 || (otherstartsentinel.precedes(first) && last.precedes(otherendsentinel))) + return MutualViewPosition.Contains; + if (otherView.size == 0 || (startsentinel.precedes(otherfirst) && otherlast.precedes(endsentinel))) + return MutualViewPosition.ContainedIn; + return MutualViewPosition.Overlapping; +#else + int end = offset + size, otherOffset = otherView.offset, otherSize = otherView.size, otherEnd = otherOffset + otherSize; + if (otherOffset >= end || otherEnd <= offset) + return MutualViewPosition.NonOverlapping; + if (size == 0 || (otherOffset <= offset && end <= otherEnd)) + return MutualViewPosition.Contains; + if (otherSize == 0 || (offset <= otherOffset && otherEnd <= end)) + return MutualViewPosition.ContainedIn; + return MutualViewPosition.Overlapping; +#endif + } + + void disposeOverlappingViews(bool reverse) + { + if (views != null) + { + foreach (HashedLinkedList<T> view in views) + { + if (view != this) + { + switch (viewPosition(view)) + { + case MutualViewPosition.ContainedIn: + if (reverse) + { } + else + view.Dispose(); + break; + case MutualViewPosition.Overlapping: + view.Dispose(); + break; + case MutualViewPosition.Contains: + case MutualViewPosition.NonOverlapping: + break; + } + } + } + } + } + + #endregion + + #endregion + + #region Constructors + + /// <summary> + /// Create a linked list with en external item equalityComparer + /// </summary> + /// <param name="itemequalityComparer">The external equalityComparer</param> + public HashedLinkedList(SCG.IEqualityComparer<T> itemequalityComparer) + : base(itemequalityComparer) + { + offset = 0; + size = stamp = 0; + startsentinel = new Node(default(T)); + endsentinel = new Node(default(T)); + startsentinel.next = endsentinel; + endsentinel.prev = startsentinel; +#if HASHINDEX + //It is important that the sentinels are different: + startsentinel.taggroup = new TagGroup(); + startsentinel.taggroup.tag = int.MinValue; + startsentinel.taggroup.count = 0; + endsentinel.taggroup = new TagGroup(); + endsentinel.taggroup.tag = int.MaxValue; + endsentinel.taggroup.count = 0; + dict = new HashDictionary<T, Node>(itemequalityComparer); +#endif + } + + /// <summary> + /// Create a linked list with the natural item equalityComparer + /// </summary> + public HashedLinkedList() : this(EqualityComparer<T>.Default) { } + + #endregion + + #region Node nested class + + /// <summary> + /// An individual cell in the linked list + /// </summary> + [Serializable] + class Node + { + public Node prev; + + public Node next; + + public T item; + + #region Tag support +#if HASHINDEX + internal int tag; + + internal TagGroup taggroup; + + internal bool precedes(Node that) + { + //Debug.Assert(taggroup != null, "taggroup field null"); + //Debug.Assert(that.taggroup != null, "that.taggroup field null"); + int t1 = taggroup.tag; + int t2 = that.taggroup.tag; + + return t1 < t2 ? true : t1 > t2 ? false : tag < that.tag; + } +#endif + #endregion + + [Tested] + internal Node(T item) { this.item = item; } + + [Tested] + internal Node(T item, Node prev, Node next) + { + this.item = item; this.prev = prev; this.next = next; + } + + public override string ToString() + { +#if HASHINDEX + return String.Format("Node: (item={0}, tag={1})", item, tag); +#else + return String.Format("Node(item={0})", item); +#endif + } + } + + #endregion + + #region Taggroup nested class and tag maintenance utilities +#if HASHINDEX + /// <summary> + /// A group of nodes with the same high tag. Purpose is to be + /// able to tell the sequence order of two nodes without having to scan through + /// the list. + /// </summary> + [Serializable] + class TagGroup + { + internal int tag, count; + + internal Node first, last; + + /// <summary> + /// Pretty print a tag group + /// </summary> + /// <returns>Formatted tag group</returns> + public override string ToString() + { return String.Format("TagGroup(tag={0}, cnt={1}, fst={2}, lst={3})", tag, count, first, last); } + } + + //Constants for tag maintenance + const int wordsize = 32; + + const int lobits = 3; + + const int hibits = lobits + 1; + + const int losize = 1 << lobits; + + const int hisize = 1 << hibits; + + const int logwordsize = 5; + + TagGroup gettaggroup(Node pred, Node succ, out int lowbound, out int highbound) + { + TagGroup predgroup = pred.taggroup, succgroup = succ.taggroup; + + if (predgroup == succgroup) + { + lowbound = pred.tag + 1; + highbound = succ.tag - 1; + return predgroup; + } + else if (predgroup.first != null) + { + lowbound = pred.tag + 1; + highbound = int.MaxValue; + return predgroup; + } + else if (succgroup.first != null) + { + lowbound = int.MinValue; + highbound = succ.tag - 1; + return succgroup; + } + else + { + lowbound = int.MinValue; + highbound = int.MaxValue; + return new TagGroup(); + } + } + + + /// <summary> + /// Put a tag on a node (already inserted in the list). Split taggroups and renumber as + /// necessary. + /// </summary> + /// <param name="node">The node to tag</param> + void settag(Node node) + { + Node pred = node.prev, succ = node.next; + TagGroup predgroup = pred.taggroup, succgroup = succ.taggroup; + + if (predgroup == succgroup) + { + node.taggroup = predgroup; + predgroup.count++; + if (pred.tag + 1 == succ.tag) + splittaggroup(predgroup); + else + node.tag = (pred.tag + 1) / 2 + (succ.tag - 1) / 2; + } + else if (predgroup.first != null) + { + node.taggroup = predgroup; + predgroup.last = node; + predgroup.count++; + if (pred.tag == int.MaxValue) + splittaggroup(predgroup); + else + node.tag = pred.tag / 2 + int.MaxValue / 2 + 1; + } + else if (succgroup.first != null) + { + node.taggroup = succgroup; + succgroup.first = node; + succgroup.count++; + if (succ.tag == int.MinValue) + splittaggroup(node.taggroup); + else + node.tag = int.MinValue / 2 + (succ.tag - 1) / 2; + } + else + { + Debug.Assert(Taggroups == 0); + + TagGroup newgroup = new TagGroup(); + + Taggroups = 1; + node.taggroup = newgroup; + newgroup.first = newgroup.last = node; + newgroup.count = 1; + return; + } + } + + + /// <summary> + /// Remove a node from its taggroup. + /// <br/> When this is called, node must already have been removed from the underlying list + /// </summary> + /// <param name="node">The node to remove</param> + void removefromtaggroup(Node node) + { + + TagGroup taggroup = node.taggroup; + + if (--taggroup.count == 0) + { + Taggroups--; + return; + } + + if (node == taggroup.first) + taggroup.first = node.next; + + if (node == taggroup.last) + taggroup.last = node.prev; + + //node.taggroup = null; + if (taggroup.count != losize || Taggroups == 1) + return; + + TagGroup otg; + // bug20070911: + Node neighbor; + if ((neighbor = taggroup.first.prev) != startsentinel + && (otg = neighbor.taggroup).count <= losize) + taggroup.first = otg.first; + else if ((neighbor = taggroup.last.next) != endsentinel + && (otg = neighbor.taggroup).count <= losize) + taggroup.last = otg.last; + else + return; + + Node n = otg.first; + + for (int i = 0, length = otg.count; i < length; i++) + { + n.taggroup = taggroup; + n = n.next; + } + + taggroup.count += otg.count; + Taggroups--; + n = taggroup.first; + + const int ofs = wordsize - hibits; + + for (int i = 0, count = taggroup.count; i < count; i++) + { + n.tag = (i - losize) << ofs; //(i-8)<<28 + n = n.next; + } + } + + + /// <summary> + /// Split a tag group to make rom for more tags. + /// </summary> + /// <param name="taggroup">The tag group</param> + void splittaggroup(TagGroup taggroup) + { + Node n = taggroup.first; + int ptgt = taggroup.first.prev.taggroup.tag; + int ntgt = taggroup.last.next.taggroup.tag; + + Debug.Assert(ptgt + 1 <= ntgt - 1); + + int ofs = wordsize - hibits; + int newtgs = (taggroup.count - 1) / hisize; + int tgtdelta = (int)((ntgt + 0.0 - ptgt) / (newtgs + 2)), tgtag = ptgt; + + tgtdelta = tgtdelta == 0 ? 1 : tgtdelta; + for (int j = 0; j < newtgs; j++) + { + TagGroup newtaggroup = new TagGroup(); + + newtaggroup.tag = (tgtag = tgtag >= ntgt - tgtdelta ? ntgt : tgtag + tgtdelta); + newtaggroup.first = n; + newtaggroup.count = hisize; + for (int i = 0; i < hisize; i++) + { + n.taggroup = newtaggroup; + n.tag = (i - losize) << ofs; //(i-8)<<28 + n = n.next; + } + + newtaggroup.last = n.prev; + } + + int rest = taggroup.count - hisize * newtgs; + + taggroup.first = n; + taggroup.count = rest; + taggroup.tag = (tgtag = tgtag >= ntgt - tgtdelta ? ntgt : tgtag + tgtdelta); ofs--; + for (int i = 0; i < rest; i++) + { + n.tag = (i - hisize) << ofs; //(i-16)<<27 + n = n.next; + } + + taggroup.last = n.prev; + Taggroups += newtgs; + if (tgtag == ntgt) + redistributetaggroups(taggroup); + } + + + private void redistributetaggroups(TagGroup taggroup) + { + TagGroup pred = taggroup, succ = taggroup, tmp; + double limit = 1, bigt = Math.Pow(Taggroups, 1.0 / 30);//????? + int bits = 1, count = 1, lowmask = 0, himask = 0, target = 0; + + do + { + bits++; + lowmask = (1 << bits) - 1; + himask = ~lowmask; + target = taggroup.tag & himask; + while ((tmp = pred.first.prev.taggroup).first != null && (tmp.tag & himask) == target) + { count++; pred = tmp; } + + while ((tmp = succ.last.next.taggroup).last != null && (tmp.tag & himask) == target) + { count++; succ = tmp; } + + limit *= bigt; + } while (count > limit); + + //redistibute tags + int lob = pred.first.prev.taggroup.tag, upb = succ.last.next.taggroup.tag; + int delta = upb / (count + 1) - lob / (count + 1); + + Debug.Assert(delta > 0); + for (int i = 0; i < count; i++) + { + pred.tag = lob + (i + 1) * delta; + pred = pred.last.next.taggroup; + } + } +#endif + + #endregion + + #region Position, PositionComparer and ViewHandler nested types + class PositionComparer : SCG.IComparer<Position> + { + static PositionComparer _default; + PositionComparer() { } + public static PositionComparer Default { get { return _default ?? (_default = new PositionComparer()); } } + public int Compare(Position a, Position b) + { +#if HASHINDEX + return a.Endpoint == b.Endpoint ? 0 : a.Endpoint.precedes(b.Endpoint) ? -1 : 1; +#else + return a.Index.CompareTo(b.Index); +#endif + } + } + /// <summary> + /// During RemoveAll, we need to cache the original endpoint indices of views + /// </summary> + struct Position + { + public readonly HashedLinkedList<T> View; + public bool Left; +#if HASHINDEX + public readonly Node Endpoint; +#else + public readonly int Index; +#endif + public Position(HashedLinkedList<T> view, bool left) + { + View = view; + Left = left; +#if HASHINDEX + Endpoint = left ? view.startsentinel.next : view.endsentinel.prev; +#else + Index = left ? view.Offset : view.Offset + view.size - 1; +#endif + } +#if HASHINDEX + public Position(Node node, int foo) { this.Endpoint = node; View = null; Left = false; } +#else + public Position(int index) { this.Index = index; View = null; Left = false; } +#endif + } + + //TODO: merge the two implementations using Position values as arguments + /// <summary> + /// Handle the update of (other) views during a multi-remove operation. + /// </summary> + struct ViewHandler + { + ArrayList<Position> leftEnds; + ArrayList<Position> rightEnds; + int leftEndIndex, rightEndIndex, leftEndIndex2, rightEndIndex2; + internal readonly int viewCount; + internal ViewHandler(HashedLinkedList<T> list) + { + leftEndIndex = rightEndIndex = leftEndIndex2 = rightEndIndex2 = viewCount = 0; + leftEnds = rightEnds = null; + if (list.views != null) + foreach (HashedLinkedList<T> v in list.views) + if (v != list) + { + if (leftEnds == null) + { + leftEnds = new ArrayList<Position>(); + rightEnds = new ArrayList<Position>(); + } + leftEnds.Add(new Position(v, true)); + rightEnds.Add(new Position(v, false)); + } + if (leftEnds == null) + return; + viewCount = leftEnds.Count; + leftEnds.Sort(PositionComparer.Default); + rightEnds.Sort(PositionComparer.Default); + } +#if HASHINDEX + internal void skipEndpoints(int removed, Node n) + { + if (viewCount > 0) + { + Position endpoint; + while (leftEndIndex < viewCount && ((endpoint = leftEnds[leftEndIndex]).Endpoint.prev.precedes(n))) + { + HashedLinkedList<T> view = endpoint.View; + view.offset = view.offset - removed;//TODO: extract offset.Value? + view.size += removed; + leftEndIndex++; + } + while (rightEndIndex < viewCount && (endpoint = rightEnds[rightEndIndex]).Endpoint.precedes(n)) + { + HashedLinkedList<T> view = endpoint.View; + view.size -= removed; + rightEndIndex++; + } + } + if (viewCount > 0) + { + Position endpoint; + while (leftEndIndex2 < viewCount && (endpoint = leftEnds[leftEndIndex2]).Endpoint.prev.precedes(n)) + leftEndIndex2++; + while (rightEndIndex2 < viewCount && (endpoint = rightEnds[rightEndIndex2]).Endpoint.next.precedes(n)) + rightEndIndex2++; + } + } + /// <summary> + /// To be called with n pointing to the right of each node to be removed in a stretch. + /// And at the endsentinel. + /// + /// Update offset of a view whose left endpoint (has not already been handled and) is n or precedes n. + /// I.e. startsentinel precedes n. + /// Also update the size as a prelude to handling the right endpoint. + /// + /// Update size of a view not already handled and whose right endpoint precedes n. + /// </summary> + /// <param name="removed">The number of nodes left of n to be removed</param> + /// <param name="n"></param> + internal void updateViewSizesAndCounts(int removed, Node n) + { + if (viewCount > 0) + { + Position endpoint; + while (leftEndIndex < viewCount && ((endpoint = leftEnds[leftEndIndex]).Endpoint.prev.precedes(n))) + { + HashedLinkedList<T> view = endpoint.View; + view.offset = view.offset - removed; //TODO: fix use of offset + view.size += removed; + leftEndIndex++; + } + while (rightEndIndex < viewCount && (endpoint = rightEnds[rightEndIndex]).Endpoint.precedes(n)) + { + HashedLinkedList<T> view = endpoint.View; + view.size -= removed; + rightEndIndex++; + } + } + } + /// <summary> + /// To be called with n being the first not-to-be-removed node after a (stretch of) node(s) to be removed. + /// + /// It will update the startsentinel of views (that have not been handled before and) + /// whose startsentinel precedes n, i.e. is to be deleted. + /// + /// It will update the endsentinel of views (...) whose endsentinel precedes n, i.e. is to be deleted. + /// + /// PROBLEM: DOESNT WORK AS ORIGINALLY ADVERTISED. WE MUST DO THIS BEFORE WE ACTUALLY REMOVE THE NODES. WHEN THE + /// NODES HAVE BEEN REMOVED, THE precedes METHOD WILL NOT WORK! + /// </summary> + /// <param name="n"></param> + /// <param name="newstart"></param> + /// <param name="newend"></param> + internal void updateSentinels(Node n, Node newstart, Node newend) + { + if (viewCount > 0) + { + Position endpoint; + while (leftEndIndex2 < viewCount && (endpoint = leftEnds[leftEndIndex2]).Endpoint.prev.precedes(n)) + { + HashedLinkedList<T> view = endpoint.View; + view.startsentinel = newstart; + leftEndIndex2++; + } + while (rightEndIndex2 < viewCount && (endpoint = rightEnds[rightEndIndex2]).Endpoint.next.precedes(n)) + { + HashedLinkedList<T> view = endpoint.View; + view.endsentinel = newend; + rightEndIndex2++; + } + } + } +#else + /// <summary> + /// This is to be called with realindex pointing to the first node to be removed after a (stretch of) node that was not removed + /// </summary> + /// <param name="removed"></param> + /// <param name="realindex"></param> + internal void skipEndpoints(int removed, int realindex) + { + if (viewCount > 0) + { + Position endpoint; + while (leftEndIndex < viewCount && (endpoint = leftEnds[leftEndIndex]).Index <= realindex) + { + HashedLinkedList<T> view = endpoint.View; + view.offset = view.offset - removed; + view.size += removed; + leftEndIndex++; + } + while (rightEndIndex < viewCount && (endpoint = rightEnds[rightEndIndex]).Index < realindex) + { + HashedLinkedList<T> view = endpoint.View; + view.size -= removed; + rightEndIndex++; + } + } + if (viewCount > 0) + { + Position endpoint; + while (leftEndIndex2 < viewCount && (endpoint = leftEnds[leftEndIndex2]).Index <= realindex) + leftEndIndex2++; + while (rightEndIndex2 < viewCount && (endpoint = rightEnds[rightEndIndex2]).Index < realindex - 1) + rightEndIndex2++; + } + } + internal void updateViewSizesAndCounts(int removed, int realindex) + { + if (viewCount > 0) + { + Position endpoint; + while (leftEndIndex < viewCount && (endpoint = leftEnds[leftEndIndex]).Index <= realindex) + { + HashedLinkedList<T> view = endpoint.View; + view.offset = view.Offset - removed; + view.size += removed; + leftEndIndex++; + } + while (rightEndIndex < viewCount && (endpoint = rightEnds[rightEndIndex]).Index < realindex) + { + HashedLinkedList<T> view = endpoint.View; + view.size -= removed; + rightEndIndex++; + } + } + } + internal void updateSentinels(int realindex, Node newstart, Node newend) + { + if (viewCount > 0) + { + Position endpoint; + while (leftEndIndex2 < viewCount && (endpoint = leftEnds[leftEndIndex2]).Index <= realindex) + { + HashedLinkedList<T> view = endpoint.View; + view.startsentinel = newstart; + leftEndIndex2++; + } + while (rightEndIndex2 < viewCount && (endpoint = rightEnds[rightEndIndex2]).Index < realindex - 1) + { + HashedLinkedList<T> view = endpoint.View; + view.endsentinel = newend; + rightEndIndex2++; + } + } + } +#endif + } + #endregion + + #region Range nested class + + class Range : DirectedCollectionValueBase<T>, IDirectedCollectionValue<T> + { + int start, count, rangestamp; + Node startnode, endnode; + + HashedLinkedList<T> list; + + bool forwards; + + + internal Range(HashedLinkedList<T> list, int start, int count, bool forwards) + { + this.list = list; this.rangestamp = list.underlying != null ? list.underlying.stamp : list.stamp; + this.start = start; this.count = count; this.forwards = forwards; + if (count > 0) + { + startnode = list.get(start); + endnode = list.get(start + count - 1); + } + } + + public override bool IsEmpty { get { list.modifycheck(rangestamp); return count == 0; } } + + [Tested] + public override int Count { [Tested]get { list.modifycheck(rangestamp); return count; } } + + + public override Speed CountSpeed { get { list.modifycheck(rangestamp); return Speed.Constant; } } + + + public override T Choose() + { + list.modifycheck(rangestamp); + if (count > 0) return startnode.item; + throw new NoSuchItemException(); + } + + + [Tested] + public override SCG.IEnumerator<T> GetEnumerator() + { + int togo = count; + + list.modifycheck(rangestamp); + if (togo == 0) + yield break; + + Node cursor = forwards ? startnode : endnode; + + yield return cursor.item; + while (--togo > 0) + { + cursor = forwards ? cursor.next : cursor.prev; + list.modifycheck(rangestamp); + yield return cursor.item; + } + } + + + [Tested] + public override IDirectedCollectionValue<T> Backwards() + { + list.modifycheck(rangestamp); + + Range b = (Range)MemberwiseClone(); + + b.forwards = !forwards; + return b; + } + + + [Tested] + IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return Backwards(); } + + + [Tested] + public override EnumerationDirection Direction + { + [Tested] + get + { return forwards ? EnumerationDirection.Forwards : EnumerationDirection.Backwards; } + } + } + + + #endregion + + #region IDisposable Members + + /// <summary> + /// Invalidate this list. If a view, just invalidate the view. + /// If not a view, invalidate the list and all views on it. + /// </summary> + public virtual void Dispose() + { + Dispose(false); + } + + void Dispose(bool disposingUnderlying) + { + if (isValid) + { + if (underlying != null) + { + isValid = false; + if (!disposingUnderlying && views != null) + views.Remove(myWeakReference); + endsentinel = null; + startsentinel = null; + underlying = null; + views = null; + myWeakReference = null; + } + else + { + //isValid = false; + //endsentinel = null; + //startsentinel = null; + if (views != null) + foreach (HashedLinkedList<T> view in views) + view.Dispose(true); + //views = null; + Clear(); + } + } + } + + #endregion IDisposable stuff + + #region IList<T> Members + + /// <summary> + /// </summary> + /// <exception cref="NoSuchItemException"> if this list is empty.</exception> + /// <value>The first item in this list.</value> + [Tested] + public virtual T First + { + [Tested] + get + { + validitycheck(); + if (size == 0) + throw new NoSuchItemException(); + return startsentinel.next.item; + } + } + + + /// <summary> + /// </summary> + /// <exception cref="NoSuchItemException"> if this list is empty.</exception> + /// <value>The last item in this list.</value> + [Tested] + public virtual T Last + { + [Tested] + get + { + validitycheck(); + if (size == 0) + throw new NoSuchItemException(); + return endsentinel.prev.item; + } + } + + /// <summary> + /// Since <code>Add(T item)</code> always add at the end of the list, + /// this describes if list has FIFO or LIFO semantics. + /// </summary> + /// <value>True if the <code>Remove()</code> operation removes from the + /// start of the list, false if it removes from the end. THe default for a new linked list is true.</value> + [Tested] + public virtual bool FIFO + { + [Tested] + get { validitycheck(); return fIFO; } + [Tested] + set { updatecheck(); fIFO = value; } + } + + /// <summary> + /// + /// </summary> + public virtual bool IsFixedSize + { + get { validitycheck(); return false; } + } + + /// <summary> + /// On this list, this indexer is read/write. + /// <exception cref="IndexOutOfRangeException"/> if i is negative or + /// >= the size of the collection. + /// </summary> + /// <value>The i'th item of this list.</value> + /// <param name="index">The index of the item to fetch or store.</param> + [Tested] + public virtual T this[int index] + { + [Tested] + get { validitycheck(); return get(index).item; } + [Tested] + set + { + updatecheck(); + Node n = get(index); + // + T item = n.item; +#if HASHINDEX + + if (itemequalityComparer.Equals(value, item)) + { + n.item = value; + dict.Update(value, n); + } + else if (!dict.FindOrAdd(value, ref n)) + { + dict.Remove(item); + n.item = value; + } + else + throw new ArgumentException("Item already in indexed list"); +#else + n.item = value; +#endif + (underlying ?? this).raiseForSetThis(index, value, item); + } + } + + /// <summary> + /// + /// </summary> + /// <value></value> + public virtual Speed IndexingSpeed { get { return Speed.Linear; } } + + /// <summary> + /// Insert an item at a specific index location in this list. + /// <exception cref="IndexOutOfRangeException"/> if i is negative or + /// > the size of the collection.</summary> + /// <param name="i">The index at which to insert.</param> + /// <param name="item">The item to insert.</param> + [Tested] + public virtual void Insert(int i, T item) + { + updatecheck(); + insert(i, i == size ? endsentinel : get(i), item); + if (ActiveEvents != EventTypeEnum.None) + (underlying ?? this).raiseForInsert(i + Offset, item); + } + + /// <summary> + /// Insert an item at the end of a compatible view, used as a pointer. + /// <para>The <code>pointer</code> must be a view on the same list as + /// <code>this</code> and the endpoitn of <code>pointer</code> must be + /// a valid insertion point of <code>this</code></para> + /// </summary> + /// <exception cref="IncompatibleViewException">If <code>pointer</code> + /// is not a view on the same list as <code>this</code></exception> + /// <exception cref="IndexOutOfRangeException"><b>??????</b> if the endpoint of + /// <code>pointer</code> is not inside <code>this</code></exception> + /// <exception cref="DuplicateNotAllowedException"> if the list has + /// <code>AllowsDuplicates==false</code> and the item is + /// already in the list.</exception> + /// <param name="pointer"></param> + /// <param name="item"></param> + public void Insert(IList<T> pointer, T item) + { + updatecheck(); + if ((pointer == null) || ((pointer.Underlying ?? pointer) != (underlying ?? this))) + throw new IncompatibleViewException(); +#warning INEFFICIENT + //TODO: make this efficient (the whole point of the method: + //Do NOT use Insert, but insert the node at pointer.endsentinel, checking + //via the ordering that this is a valid insertion point + Insert(pointer.Offset + pointer.Count - Offset, item); + } + + /// <summary> + /// Insert into this list all items from an enumerable collection starting + /// at a particular index. + /// <exception cref="IndexOutOfRangeException"/> if i is negative or + /// > the size of the collection. + /// </summary> + /// <param name="i">Index to start inserting at</param> + /// <param name="items">Items to insert</param> + /// <typeparam name="U"></typeparam> + [Tested] + public virtual void InsertAll<U>(int i, SCG.IEnumerable<U> items) where U : T + { + insertAll(i, items, true); + } + + void insertAll<U>(int i, SCG.IEnumerable<U> items, bool insertion) where U : T + { + updatecheck(); + Node succ, node, pred; + int count = 0; + succ = i == size ? endsentinel : get(i); + pred = node = succ.prev; +#if HASHINDEX + TagGroup taggroup = null; + int taglimit = 0, thetag = 0; + taggroup = gettaggroup(node, succ, out thetag, out taglimit); + try + { + foreach (T item in items) + { + Node tmp = new Node(item, node, null); + if (!dict.FindOrAdd(item, ref tmp)) + { + tmp.tag = thetag < taglimit ? ++thetag : thetag; + tmp.taggroup = taggroup; + node.next = tmp; + count++; + node = tmp; + } + else + throw new DuplicateNotAllowedException("Item already in indexed list"); + } + } + finally + { + if (count != 0) + { + taggroup.count += count; + if (taggroup != pred.taggroup) + taggroup.first = pred.next; + if (taggroup != succ.taggroup) + taggroup.last = node; + succ.prev = node; + node.next = succ; + if (node.tag == node.prev.tag) + splittaggroup(taggroup); + size += count; + if (underlying != null) + underlying.size += count; + fixViewsAfterInsert(succ, pred, count, 0); + raiseForInsertAll(pred, i, count, insertion); + } + } +#else + foreach (T item in items) + { + Node tmp = new Node(item, node, null); + node.next = tmp; + count++; + node = tmp; + } + if (count == 0) + return; + succ.prev = node; + node.next = succ; + size += count; + if (underlying != null) + underlying.size += count; + if (count > 0) + { + fixViewsAfterInsert(succ, pred, count, offset + i); + raiseForInsertAll(pred, i, count, insertion); + } +#endif + } + + private void raiseForInsertAll(Node node, int i, int added, bool insertion) + { + if (ActiveEvents != 0) + { + int index = Offset + i; + if ((ActiveEvents & (EventTypeEnum.Added | EventTypeEnum.Inserted)) != 0) + for (int j = index; j < index + added; j++) + { +#warning must we check stamps here? + node = node.next; + T item = node.item; + if (insertion) raiseItemInserted(item, j); + raiseItemsAdded(item, 1); + } + raiseCollectionChanged(); + } + } + + /// <summary> + /// Insert an item at the front of this list. + /// </summary> + /// <param name="item">The item to insert.</param> + [Tested] + public virtual void InsertFirst(T item) + { + updatecheck(); + insert(0, startsentinel.next, item); + if (ActiveEvents != EventTypeEnum.None) + (underlying ?? this).raiseForInsert(0 + Offset, item); + } + + /// <summary> + /// Insert an item at the back of this list. + /// </summary> + /// <param name="item">The item to insert.</param> + [Tested] + public virtual void InsertLast(T item) + { + updatecheck(); + insert(size, endsentinel, item); + if (ActiveEvents != EventTypeEnum.None) + (underlying ?? this).raiseForInsert(size - 1 + Offset, item); + } + + /// <summary> + /// Create a new list consisting of the results of mapping all items of this + /// list. + /// </summary> + /// <param name="mapper">The delegate defining the map.</param> + /// <returns>The new list.</returns> + [Tested] + public IList<V> Map<V>(Fun<T, V> mapper) + { + validitycheck(); + + HashedLinkedList<V> retval = new HashedLinkedList<V>(); + return map<V>(mapper, retval); + } + + /// <summary> + /// Create a new list consisting of the results of mapping all items of this + /// list. The new list will use a specified equalityComparer for the item type. + /// </summary> + /// <typeparam name="V">The type of items of the new list</typeparam> + /// <param name="mapper">The delegate defining the map.</param> + /// <param name="equalityComparer">The equalityComparer to use for the new list</param> + /// <returns>The new list.</returns> + public IList<V> Map<V>(Fun<T, V> mapper, SCG.IEqualityComparer<V> equalityComparer) + { + validitycheck(); + + HashedLinkedList<V> retval = new HashedLinkedList<V>(equalityComparer); + return map<V>(mapper, retval); + } + + private IList<V> map<V>(Fun<T, V> mapper, HashedLinkedList<V> retval) + { + if (size == 0) + return retval; + int stamp = this.stamp; + Node cursor = startsentinel.next; + HashedLinkedList<V>.Node mcursor = retval.startsentinel; + +#if HASHINDEX + double tagdelta = int.MaxValue / (size + 1.0); + int count = 1; + HashedLinkedList<V>.TagGroup taggroup = null; + taggroup = new HashedLinkedList<V>.TagGroup(); + retval.taggroups = 1; + taggroup.count = size; +#endif + while (cursor != endsentinel) + { + V v = mapper(cursor.item); + modifycheck(stamp); + mcursor.next = new HashedLinkedList<V>.Node(v, mcursor, null); + cursor = cursor.next; + mcursor = mcursor.next; +#if HASHINDEX + retval.dict.Add(v, mcursor); + mcursor.taggroup = taggroup; + mcursor.tag = (int)(tagdelta * count++); +#endif + } + +#if HASHINDEX + taggroup.first = retval.startsentinel.next; + taggroup.last = mcursor; +#endif + retval.endsentinel.prev = mcursor; + mcursor.next = retval.endsentinel; + retval.size = size; + return retval; + } + + /// <summary> + /// Remove one item from the list: from the front if <code>FIFO</code> + /// is true, else from the back. + /// <exception cref="NoSuchItemException"/> if this list is empty. + /// </summary> + /// <returns>The removed item.</returns> + [Tested] + public virtual T Remove() + { + updatecheck(); + if (size == 0) + throw new NoSuchItemException("List is empty"); + T item = fIFO ? remove(startsentinel.next, 0) : remove(endsentinel.prev, size - 1); +#if HASHINDEX + dict.Remove(item); +#endif + (underlying ?? this).raiseForRemove(item); + return item; + } + + /// <summary> + /// Remove one item from the front of the list. + /// <exception cref="NoSuchItemException"/> if this list is empty. + /// </summary> + /// <returns>The removed item.</returns> + [Tested] + public virtual T RemoveFirst() + { + updatecheck(); + if (size == 0) + throw new NoSuchItemException("List is empty"); + + T item = remove(startsentinel.next, 0); +#if HASHINDEX + dict.Remove(item); +#endif + if (ActiveEvents != EventTypeEnum.None) + (underlying ?? this).raiseForRemoveAt(Offset, item); + return item; + } + + /// <summary> + /// Remove one item from the back of the list. + /// <exception cref="NoSuchItemException"/> if this list is empty. + /// </summary> + /// <returns>The removed item.</returns> + [Tested] + public virtual T RemoveLast() + { + updatecheck(); + if (size == 0) + throw new NoSuchItemException("List is empty"); + + T item = remove(endsentinel.prev, size - 1); +#if HASHINDEX + dict.Remove(item); +#endif + if (ActiveEvents != EventTypeEnum.None) + (underlying ?? this).raiseForRemoveAt(size + Offset, item); + return item; + } + + /// <summary> + /// Create a list view on this list. + /// </summary> + /// <exception cref="ArgumentOutOfRangeException"> if the start or count is negative</exception> + /// <exception cref="ArgumentException"> if the range does not fit within list.</exception> + /// <param name="start">The index in this list of the start of the view.</param> + /// <param name="count">The size of the view.</param> + /// <returns>The new list view.</returns> + [Tested] + public virtual IList<T> View(int start, int count) + { + checkRange(start, count); + validitycheck(); + if (views == null) + views = new WeakViewList<HashedLinkedList<T>>(); + HashedLinkedList<T> retval = (HashedLinkedList<T>)MemberwiseClone(); + retval.underlying = underlying != null ? underlying : this; + retval.offset = offset + start; + retval.size = count; + getPair(start - 1, start + count, out retval.startsentinel, out retval.endsentinel, + new int[] { -1, size }, new Node[] { startsentinel, endsentinel }); + //retval.startsentinel = start == 0 ? startsentinel : get(start - 1); + //retval.endsentinel = start + count == size ? endsentinel : get(start + count); + + //TODO: for the purpose of Dispose, we need to retain a ref to the node + retval.myWeakReference = views.Add(retval); + return retval; + } + + /// <summary> + /// Create a list view on this list containing the (first) occurrence of a particular item. + /// </summary> + /// <exception cref="ArgumentException"> if the item is not in this list.</exception> + /// <param name="item">The item to find.</param> + /// <returns>The new list view.</returns> + public virtual IList<T> ViewOf(T item) + { +#if HASHINDEX + Node n; + validitycheck(); + if (!contains(item, out n)) + return null; + HashedLinkedList<T> retval = (HashedLinkedList<T>)MemberwiseClone(); + retval.underlying = underlying != null ? underlying : this; + retval.offset = null; + retval.startsentinel = n.prev; + retval.endsentinel = n.next; + retval.size = 1; + return retval; +#else + int index = 0; + Node n = startsentinel.next; + if (!find(item, ref n, ref index)) + return null; + //TODO: optimize with getpair! + return View(index, 1); +#endif + } + + /// <summary> + /// Create a list view on this list containing the last occurrence of a particular item. + /// <exception cref="ArgumentException"/> if the item is not in this list. + /// </summary> + /// <param name="item">The item to find.</param> + /// <returns>The new list view.</returns> + public virtual IList<T> LastViewOf(T item) + { +#if HASHINDEX + return ViewOf(item); +#else + int index = size - 1; + Node n = endsentinel.prev; + if (!dnif(item, ref n, ref index)) + return null; + return View(index, 1); +#endif + } + + /// <summary> + /// Null if this list is not a view. + /// </summary> + /// <value>Underlying list for view.</value> + [Tested] + public virtual IList<T> Underlying { [Tested]get { validitycheck(); return underlying; } } + + /// <summary> + /// + /// </summary> + /// <value></value> + public virtual bool IsValid { get { return isValid; } } + + /// <summary> + /// </summary> + /// <value>Offset for this list view or 0 for a underlying list.</value> + [Tested] + public virtual int Offset + { + [Tested] + get + { + validitycheck(); +#if HASHINDEX + if (offset == null && underlying != null) + { + //TODO: search from both ends simultaneously! + Node n = underlying.startsentinel; + int i = 0; + while (n != startsentinel) { n = n.next; i++; } + offset = i; + } +#endif + return (int)offset; + } + } + + /// <summary> + /// Slide this list view along the underlying list. + /// </summary> + /// <exception cref="NotAViewException"> if this list is not a view.</exception> + /// <exception cref="ArgumentOutOfRangeException"> if the operation + /// would bring either end of the view outside the underlying list.</exception> + /// <param name="offset">The signed amount to slide: positive to slide + /// towards the end.</param> + [Tested] + public IList<T> Slide(int offset) + { + if (!TrySlide(offset, size)) + throw new ArgumentOutOfRangeException(); + return this; + } + + //TODO: more test cases + /// <summary> + /// Slide this list view along the underlying list, perhaps changing its size. + /// </summary> + /// <exception cref="NotAViewException"> if this list is not a view.</exception> + /// <exception cref="ArgumentOutOfRangeException"> if the operation + /// would bring either end of the view outside the underlying list.</exception> + /// <param name="offset">The signed amount to slide: positive to slide + /// towards the end.</param> + /// <param name="size">The new size of the view.</param> + public IList<T> Slide(int offset, int size) + { + if (!TrySlide(offset, size)) + throw new ArgumentOutOfRangeException(); + return this; + } + + /// <summary> + /// + /// </summary> + /// <param name="offset"></param> + /// <returns></returns> + public virtual bool TrySlide(int offset) { return TrySlide(offset, size); } + + /// <summary> + /// + /// </summary> + /// <param name="offset"></param> + /// <param name="size"></param> + /// <returns></returns> + public virtual bool TrySlide(int offset, int size) + { + updatecheck(); + if (underlying == null) + throw new NotAViewException("List not a view"); + +#pragma warning disable 472 + if (this.offset == null) //Note: only possible with HASHINDEX +#pragma warning restore 472 + { +#pragma warning disable 162 + try + { + getPair(offset - 1, offset + size, out startsentinel, out endsentinel, + new int[] { -1, this.size }, new Node[] { startsentinel, endsentinel }); + //TODO: maybe-update offset field + } + catch (NullReferenceException) + { + return false; + } +#pragma warning restore 162 + } + else + { + if (offset + this.offset < 0 || offset + this.offset + size > underlying.size) + return false; + int oldoffset = (int)(this.offset); + getPair(offset - 1, offset + size, out startsentinel, out endsentinel, + new int[] { -oldoffset - 1, -1, this.size, underlying.size - oldoffset }, + new Node[] { underlying.startsentinel, startsentinel, endsentinel, underlying.endsentinel }); + } + this.size = size; + this.offset += offset; + return true; + } + + + //TODO: improve the complexity of the implementation + /// <summary> + /// + /// <para>Returns null if <code>otherView</code> is strictly to the left of this view</para> + /// </summary> + /// <param name="otherView"></param> + /// <exception cref="IncompatibleViewException">If otherView does not have the same underlying list as this</exception> + /// <returns></returns> + public virtual IList<T> Span(IList<T> otherView) + { + if ((otherView == null) || ((otherView.Underlying ?? otherView) != (underlying ?? this))) + throw new IncompatibleViewException(); + if (otherView.Offset + otherView.Count - Offset < 0) + return null; + return (underlying ?? this).View(Offset, otherView.Offset + otherView.Count - Offset); + } + + + //Question: should we swap items or move nodes around? + //The first seems much more efficient unless the items are value types + //with a large memory footprint. + //(Swapping will do count*3/2 T assignments, linking around will do + // 4*count ref assignments; note that ref assignments are more expensive + //than copying non-ref bits) + /// <summary> + /// Reverse the list so the items are in the opposite sequence order. + /// </summary> + [Tested] + public virtual void Reverse() + { + updatecheck(); + if (size == 0) + return; + + Position[] positions = null; + int poslow = 0, poshigh = 0; + if (views != null) + { + CircularQueue<Position> _positions = null; + foreach (HashedLinkedList<T> view in views) + { + if (view != this) + { + switch (viewPosition(view)) + { + case MutualViewPosition.ContainedIn: + (_positions ?? (_positions = new CircularQueue<Position>())).Enqueue(new Position(view, true)); + _positions.Enqueue(new Position(view, false)); + break; + case MutualViewPosition.Overlapping: + view.Dispose(); + break; + case MutualViewPosition.Contains: + case MutualViewPosition.NonOverlapping: + break; + } + } + } + if (_positions != null) + { + positions = _positions.ToArray(); + Sorting.IntroSort<Position>(positions, 0, positions.Length, PositionComparer.Default); + poshigh = positions.Length - 1; + } + } + + Node a = get(0), b = get(size - 1); + for (int i = 0; i < size / 2; i++) + { + T swap; + swap = a.item; a.item = b.item; b.item = swap; +#if HASHINDEX + dict[a.item] = a; dict[b.item] = b; +#endif + if (positions != null) + mirrorViewSentinelsForReverse(positions, ref poslow, ref poshigh, a, b, i); + a = a.next; b = b.prev; + } + if (positions != null && size % 2 != 0) + mirrorViewSentinelsForReverse(positions, ref poslow, ref poshigh, a, b, size / 2); + (underlying ?? this).raiseCollectionChanged(); + } + + private void mirrorViewSentinelsForReverse(Position[] positions, ref int poslow, ref int poshigh, Node a, Node b, int i) + { +#if HASHINDEX + int? aindex = offset + i, bindex = offset + size - 1 - i; +#else + int aindex = offset + i, bindex = offset + size - 1 - i; +#endif + Position pos; +#if HASHINDEX + while (poslow <= poshigh && (pos = positions[poslow]).Endpoint == a) +#else + while (poslow <= poshigh && (pos = positions[poslow]).Index == aindex) +#endif + { + //TODO: Note: in the case og hashed linked list, if this.offset == null, but pos.View.offset!=null + //we may at this point compute this.offset and non-null values of aindex and bindex + if (pos.Left) + pos.View.endsentinel = b.next; + else + { + pos.View.startsentinel = b.prev; + pos.View.offset = bindex; + } + poslow++; + } +#if HASHINDEX + while (poslow < poshigh && (pos = positions[poshigh]).Endpoint == b) +#else + while (poslow < poshigh && (pos = positions[poshigh]).Index == bindex) +#endif + { + if (pos.Left) + pos.View.endsentinel = a.next; + else + { + pos.View.startsentinel = a.prev; + pos.View.offset = aindex; + } + poshigh--; + } + } + + /// <summary> + /// Check if this list is sorted according to the default sorting order + /// for the item type T, as defined by the <see cref="T:C5.Comparer`1"/> class + /// </summary> + /// <exception cref="NotComparableException">if T is not comparable</exception> + /// <returns>True if the list is sorted, else false.</returns> + public bool IsSorted() { return IsSorted(Comparer<T>.Default); } + + /// <summary> + /// Check if this list is sorted according to a specific sorting order. + /// </summary> + /// <param name="c">The comparer defining the sorting order.</param> + /// <returns>True if the list is sorted, else false.</returns> + [Tested] + public virtual bool IsSorted(SCG.IComparer<T> c) + { + validitycheck(); + if (size <= 1) + return true; + + Node node = startsentinel.next; + T prevItem = node.item; + + node = node.next; + while (node != endsentinel) + { + if (c.Compare(prevItem, node.item) > 0) + return false; + else + { + prevItem = node.item; + node = node.next; + } + } + + return true; + } + + /// <summary> + /// Sort the items of the list according to the default sorting order + /// for the item type T, as defined by the Comparer[T] class. + /// (<see cref="T:C5.Comparer`1"/>). + /// The sorting is stable. + /// </summary> + /// <exception cref="InvalidOperationException">if T is not comparable</exception> + public virtual void Sort() { Sort(Comparer<T>.Default); } + + // Sort the linked list using mergesort + /// <summary> + /// Sort the items of the list according to a specific sorting order. + /// The sorting is stable. + /// </summary> + /// <param name="c">The comparer defining the sorting order.</param> + [Tested] + public virtual void Sort(SCG.IComparer<T> c) + { + updatecheck(); + if (size == 0) + return; + disposeOverlappingViews(false); +#if HASHINDEX + if (underlying != null) + { + Node cursor = startsentinel.next; + while (cursor != endsentinel) + { + cursor.taggroup.count--; + cursor = cursor.next; + } + } +#endif + // Build a linked list of non-empty runs. + // The prev field in first node of a run points to next run's first node + Node runTail = startsentinel.next; + Node prevNode = startsentinel.next; + + endsentinel.prev.next = null; + while (prevNode != null) + { + Node node = prevNode.next; + + while (node != null && c.Compare(prevNode.item, node.item) <= 0) + { + prevNode = node; + node = prevNode.next; + } + + // Completed a run; prevNode is the last node of that run + prevNode.next = null; // Finish the run + runTail.prev = node; // Link it into the chain of runs + runTail = node; + if (c.Compare(endsentinel.prev.item, prevNode.item) <= 0) + endsentinel.prev = prevNode; // Update last pointer to point to largest + + prevNode = node; // Start a new run + } + + // Repeatedly merge runs two and two, until only one run remains + while (startsentinel.next.prev != null) + { + Node run = startsentinel.next; + Node newRunTail = null; + + while (run != null && run.prev != null) + { // At least two runs, merge + Node nextRun = run.prev.prev; + Node newrun = mergeRuns(run, run.prev, c); + + if (newRunTail != null) + newRunTail.prev = newrun; + else + startsentinel.next = newrun; + + newRunTail = newrun; + run = nextRun; + } + + if (run != null) // Add the last run, if any + newRunTail.prev = run; + } + + endsentinel.prev.next = endsentinel; + startsentinel.next.prev = startsentinel; + + //assert invariant(); + //assert isSorted(); +#if HASHINDEX + { + Node cursor = startsentinel.next, end = endsentinel; + int tag, taglimit; + TagGroup t = gettaggroup(startsentinel, endsentinel, out tag, out taglimit); + int tagdelta = taglimit / (size + 1) - tag / (size + 1); + tagdelta = tagdelta == 0 ? 1 : tagdelta; + if (underlying == null) + taggroups = 1; + while (cursor != end) + { + tag = tag + tagdelta > taglimit ? taglimit : tag + tagdelta; + cursor.tag = tag; + t.count++; + cursor.taggroup = t; + cursor = cursor.next; + } + if (t != startsentinel.taggroup) + t.first = startsentinel.next; + if (t != endsentinel.taggroup) + t.last = endsentinel.prev; + if (tag == taglimit) + splittaggroup(t); + } +#endif + (underlying ?? this).raiseCollectionChanged(); + } + + private static Node mergeRuns(Node run1, Node run2, SCG.IComparer<T> c) + { + //assert run1 != null && run2 != null; + Node prev; + bool prev1; // is prev from run1? + + if (c.Compare(run1.item, run2.item) <= 0) + { + prev = run1; + prev1 = true; + run1 = run1.next; + } + else + { + prev = run2; + prev1 = false; + run2 = run2.next; + } + + Node start = prev; + + //assert start != null; + start.prev = null; + while (run1 != null && run2 != null) + { + if (prev1) + { + //assert prev.next == run1; + //Comparable run2item = (Comparable)run2.item; + while (run1 != null && c.Compare(run2.item, run1.item) >= 0) + { + prev = run1; + run1 = prev.next; + } + + if (run1 != null) + { // prev.item <= run2.item < run1.item; insert run2 + prev.next = run2; + run2.prev = prev; + prev = run2; + run2 = prev.next; + prev1 = false; + } + } + else + { + //assert prev.next == run2; + //Comparable run1item = (Comparable)run1.item; + while (run2 != null && c.Compare(run1.item, run2.item) > 0) + { + prev = run2; + run2 = prev.next; + } + + if (run2 != null) + { // prev.item < run1.item <= run2.item; insert run1 + prev.next = run1; + run1.prev = prev; + prev = run1; + run1 = prev.next; + prev1 = true; + } + } + } + + //assert !(run1 != null && prev1) && !(run2 != null && !prev1); + if (run1 != null) + { // last run2 < all of run1; attach run1 at end + prev.next = run1; + run1.prev = prev; + } + else if (run2 != null) + { // last run1 + prev.next = run2; + run2.prev = prev; + } + + return start; + } + + /// <summary> + /// Randomly shuffle the items of this list. + /// <para>Will invalidate overlapping views???</para> + /// </summary> + public virtual void Shuffle() { Shuffle(new C5Random()); } + + + /// <summary> + /// Shuffle the items of this list according to a specific random source. + /// <para>Will invalidate overlapping views???</para> + /// </summary> + /// <param name="rnd">The random source.</param> + public virtual void Shuffle(Random rnd) + { + updatecheck(); + if (size == 0) + return; + disposeOverlappingViews(false); + ArrayList<T> a = new ArrayList<T>(); + a.AddAll(this); + a.Shuffle(rnd); + Node cursor = startsentinel.next; + int j = 0; + while (cursor != endsentinel) + { + cursor.item = a[j++]; +#if HASHINDEX + dict[cursor.item] = cursor; +#endif + cursor = cursor.next; + } + (underlying ?? this).raiseCollectionChanged(); + } + + #endregion + + #region IIndexed<T> Members + + /// <summary> + /// <exception cref="IndexOutOfRangeException"/>. + /// </summary> + /// <value>The directed collection of items in a specific index interval.</value> + /// <param name="start">The low index of the interval (inclusive).</param> + /// <param name="count">The size of the range.</param> + [Tested] + public IDirectedCollectionValue<T> this[int start, int count] + { + [Tested] + get + { + validitycheck(); + checkRange(start, count); + return new Range(this, start, count, true); + } + } + + /// <summary> + /// Searches for an item in the list going forwrds from the start. + /// </summary> + /// <param name="item">Item to search for.</param> + /// <returns>Index of item from start.</returns> + [Tested] + public virtual int IndexOf(T item) + { + validitycheck(); + Node node; +#if HASHINDEX + if (!dict.Find(item, out node) || !insideview(node)) + return ~size; +#endif + node = startsentinel.next; + int index = 0; + if (find(item, ref node, ref index)) + return index; + else + return ~size; + } + + /// <summary> + /// Searches for an item in the list going backwords from the end. + /// </summary> + /// <param name="item">Item to search for.</param> + /// <returns>Index of of item from the end.</returns> + [Tested] + public virtual int LastIndexOf(T item) + { +#if HASHINDEX + return IndexOf(item); +#else + validitycheck(); + + Node node = endsentinel.prev; + int index = size - 1; + + if (dnif(item, ref node, ref index)) + return index; + else + return ~size; +#endif + } + + /// <summary> + /// Remove the item at a specific position of the list. + /// <exception cref="IndexOutOfRangeException"/> if i is negative or + /// >= the size of the collection. + /// </summary> + /// <param name="i">The index of the item to remove.</param> + /// <returns>The removed item.</returns> + [Tested] + public virtual T RemoveAt(int i) + { + updatecheck(); + T retval = remove(get(i), i); +#if HASHINDEX + dict.Remove(retval); +#endif + if (ActiveEvents != EventTypeEnum.None) + (underlying ?? this).raiseForRemoveAt(Offset + i, retval); + return retval; + } + + /// <summary> + /// Remove all items in an index interval. + /// <exception cref="IndexOutOfRangeException"/>???. + /// </summary> + /// <param name="start">The index of the first item to remove.</param> + /// <param name="count">The number of items to remove.</param> + [Tested] + public virtual void RemoveInterval(int start, int count) + { +#if HASHINDEX + updatecheck(); + checkRange(start, count); + if (count == 0) + return; + + View(start, count).Clear(); +#else + //Note: this is really almost equaivalent to Clear on a view + updatecheck(); + checkRange(start, count); + if (count == 0) + return; + + //for small count: optimize + //use an optimal get(int i, int j, ref Node ni, ref Node nj)? + Node a = get(start), b = get(start + count - 1); + fixViewsBeforeRemove(start, count, a, b); + a.prev.next = b.next; + b.next.prev = a.prev; + if (underlying != null) + underlying.size -= count; + + size -= count; + if (ActiveEvents != EventTypeEnum.None) + (underlying ?? this).raiseForRemoveInterval(start + Offset, count); +#endif + } + + void raiseForRemoveInterval(int start, int count) + { + if (ActiveEvents != 0) + { + raiseCollectionCleared(size == 0, count, start); + raiseCollectionChanged(); + } + } + #endregion + + #region ISequenced<T> Members + + /// <summary> + /// + /// </summary> + /// <returns></returns> + [Tested] + public override int GetSequencedHashCode() { validitycheck(); return base.GetSequencedHashCode(); } + + /// <summary> + /// + /// </summary> + /// <param name="that"></param> + /// <returns></returns> + [Tested] + public override bool SequencedEquals(ISequenced<T> that) { validitycheck(); return base.SequencedEquals(that); } + + #endregion + + #region IDirectedCollection<T> Members + + /// <summary> + /// Create a collection containing the same items as this collection, but + /// whose enumerator will enumerate the items backwards. The new collection + /// will become invalid if the original is modified. Method typicaly used as in + /// <code>foreach (T x in coll.Backwards()) {...}</code> + /// </summary> + /// <returns>The backwards collection.</returns> + [Tested] + public override IDirectedCollectionValue<T> Backwards() + { return this[0, size].Backwards(); } + + #endregion + + #region IDirectedEnumerable<T> Members + + [Tested] + IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return Backwards(); } + + #endregion + + #region IEditableCollection<T> Members + + /// <summary> + /// The value is symbolic indicating the type of asymptotic complexity + /// in terms of the size of this collection (worst-case or amortized as + /// relevant). + /// </summary> + /// <value>Speed.Linear</value> + [Tested] + public virtual Speed ContainsSpeed + { + [Tested] + get + { +#if HASHINDEX + return Speed.Constant; +#else + return Speed.Linear; +#endif + } + } + + /// <summary> + /// Performs a check for view validity before calling base.GetUnsequencedHashCode() + /// </summary> + /// <returns></returns> + [Tested] + public override int GetUnsequencedHashCode() + { validitycheck(); return base.GetUnsequencedHashCode(); } + + /// <summary> + /// + /// </summary> + /// <param name="that"></param> + /// <returns></returns> + [Tested] + public override bool UnsequencedEquals(ICollection<T> that) + { validitycheck(); return base.UnsequencedEquals(that); } + + /// <summary> + /// Check if this collection contains (an item equivalent to according to the + /// itemequalityComparer) a particular value. + /// </summary> + /// <param name="item">The value to check for.</param> + /// <returns>True if the items is in this collection.</returns> + [Tested] + public virtual bool Contains(T item) + { + validitycheck(); + Node node; + return contains(item, out node); + } + + /// <summary> + /// Check if this collection contains an item equivalent according to the + /// itemequalityComparer to a particular value. If so, return in the ref argument (a + /// binary copy of) the actual value found. + /// </summary> + /// <param name="item">The value to look for.</param> + /// <returns>True if the items is in this collection.</returns> + [Tested] + public virtual bool Find(ref T item) + { + validitycheck(); + Node node; + if (contains(item, out node)) { item = node.item; return true; } + return false; + } + + /// <summary> + /// Check if this collection contains an item equivalent according to the + /// itemequalityComparer to a particular value. If so, update the item in the collection + /// to with a binary copy of the supplied value. Will update a single item. + /// </summary> + /// <param name="item">Value to update.</param> + /// <returns>True if the item was found and hence updated.</returns> + [Tested] + public virtual bool Update(T item) { T olditem; return Update(item, out olditem); } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <param name="olditem"></param> + /// <returns></returns> + public virtual bool Update(T item, out T olditem) + { + updatecheck(); + Node node; + + if (contains(item, out node)) + { + olditem = node.item; + node.item = item; +#if HASHINDEX + //Avoid clinging onto a reference to olditem via dict! + dict.Update(item, node); +#endif + (underlying ?? this).raiseForUpdate(item, olditem); + return true; + } + + olditem = default(T); + return false; + } + + /// <summary> + /// Check if this collection contains an item equivalent according to the + /// itemequalityComparer to a particular value. If so, return in the ref argument (a + /// binary copy of) the actual value found. Else, add the item to the collection. + /// </summary> + /// <param name="item">The value to look for.</param> + /// <returns>True if the item was found (hence not added).</returns> + [Tested] + public virtual bool FindOrAdd(ref T item) + { + updatecheck(); +#if HASHINDEX + //This is an extended myinsert: + Node node = new Node(item); + if (!dict.FindOrAdd(item, ref node)) + { + insertNode(true, endsentinel, node); + (underlying ?? this).raiseForAdd(item); + return false; + } + if (!insideview(node)) + throw new ArgumentException("Item alredy in indexed list but outside view"); + item = node.item; + return true; +#else + if (Find(ref item)) + return true; + + Add(item); + return false; +#endif + } + + /// <summary> + /// Check if this collection contains an item equivalent according to the + /// itemequalityComparer to a particular value. If so, update the item in the collection + /// to with a binary copy of the supplied value; else add the value to the collection. + /// </summary> + /// <param name="item">Value to add or update.</param> + /// <returns>True if the item was found and updated (hence not added).</returns> + [Tested] + public virtual bool UpdateOrAdd(T item) { T olditem; return UpdateOrAdd(item, out olditem); } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <param name="olditem"></param> + /// <returns></returns> + public virtual bool UpdateOrAdd(T item, out T olditem) + { + updatecheck(); +#if HASHINDEX + Node node = new Node(item); + //NOTE: it is hard to do this without double access to the dictionary + //in the update case + if (dict.FindOrAdd(item, ref node)) + { + if (!insideview(node)) + throw new ArgumentException("Item in indexed list but outside view"); + olditem = node.item; + //Avoid clinging onto a reference to olditem via dict! + dict.Update(item, node); + node.item = item; + (underlying ?? this).raiseForUpdate(item, olditem); + return true; + } + insertNode(true, endsentinel, node); + (underlying ?? this).raiseForAdd(item); +#else + if (Update(item, out olditem)) + return true; + Add(item); +#endif + olditem = default(T); + return false; + } + + /// <summary> + /// Remove a particular item from this collection. Since the collection has bag + /// semantics only one copy equivalent to the supplied item is removed. + /// </summary> + /// <param name="item">The value to remove.</param> + /// <returns>True if the item was found (and removed).</returns> + [Tested] + public virtual bool Remove(T item) + { + updatecheck(); + int i = 0; + Node node; +#if HASHINDEX + if (!dictremove(item, out node)) +#else + node = fIFO ? startsentinel.next : endsentinel.prev; + if (!(fIFO ? find(item, ref node, ref i) : dnif(item, ref node, ref i))) +#endif + return false; + T removeditem = remove(node, i); + (underlying ?? this).raiseForRemove(removeditem); + return true; + } + + /// <summary> + /// Remove a particular item from this collection if found (only one copy). + /// If an item was removed, report a binary copy of the actual item removed in + /// the argument. + /// </summary> + /// <param name="item">The value to remove on input.</param> + /// <param name="removeditem">The value removed.</param> + /// <returns>True if the item was found (and removed).</returns> + [Tested] + public virtual bool Remove(T item, out T removeditem) + { + updatecheck(); + int i = 0; + Node node; +#if HASHINDEX + if (!dictremove(item, out node)) +#else + node = fIFO ? startsentinel.next : endsentinel.prev; + if (!(fIFO ? find(item, ref node, ref i) : dnif(item, ref node, ref i))) +#endif + { + removeditem = default(T); + return false; + } + removeditem = node.item; + remove(node, i); + (underlying ?? this).raiseForRemove(removeditem); + return true; + } + + /// <summary> + /// Remove all items in another collection from this one, taking multiplicities into account. + /// <para>Always removes from the front of the list. + /// </para> + /// <para>The asymptotic running time complexity of this method is <code>O(n+m+v*log(v))</code>, + /// where <code>n</code> is the size of this list, <code>m</code> is the size of the + /// <code>items</code> collection and <code>v</code> is the number of views. + /// The method will temporarily allocate memory of size <code>O(m+v)</code>. + /// </para> + /// </summary> + /// <typeparam name="U"></typeparam> + /// <param name="items">The items to remove.</param> + [Tested] + public virtual void RemoveAll<U>(SCG.IEnumerable<U> items) where U : T + { + updatecheck(); + if (size == 0) + return; + RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this); + bool mustFire = raiseHandler.MustFire; +#if HASHINDEX + Node node; + foreach (T item in items) + if (dictremove(item, out node)) + { + if (mustFire) + raiseHandler.Remove(node.item); + remove(node, 118); + } +#else + HashBag<T> toremove = new HashBag<T>(itemequalityComparer); + toremove.AddAll(items); + ViewHandler viewHandler = new ViewHandler(this); + int index = 0, removed = 0, myoffset = Offset; + Node node = startsentinel.next; + while (node != endsentinel) + { + //pass by a stretch of nodes + while (node != endsentinel && !toremove.Contains(node.item)) + { + node = node.next; + index++; + } + viewHandler.skipEndpoints(removed, myoffset + index); + //Remove a stretch of nodes + Node localend = node.prev; //Latest node not to be removed + while (node != endsentinel && toremove.Remove(node.item)) + { + if (mustFire) + raiseHandler.Remove(node.item); + removed++; + node = node.next; + index++; + viewHandler.updateViewSizesAndCounts(removed, myoffset + index); + } + viewHandler.updateSentinels(myoffset + index, localend, node); + localend.next = node; + node.prev = localend; + } + index = underlying != null ? underlying.size + 1 - myoffset : size + 1 - myoffset; + viewHandler.updateViewSizesAndCounts(removed, myoffset + index); + size -= removed; + if (underlying != null) + underlying.size -= removed; +#endif + raiseHandler.Raise(); + } + + /// <summary> + /// + /// </summary> + /// <param name="predicate"></param> + void RemoveAll(Fun<T, bool> predicate) + { + updatecheck(); + if (size == 0) + return; + RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this); + bool mustFire = raiseHandler.MustFire; +#if HASHINDEX + { + Node n = startsentinel.next; + + while (n != endsentinel) + { + bool removeIt = predicate(n.item); + updatecheck(); + if (removeIt) + { + dict.Remove(n.item); + remove(n, 119); + if (mustFire) + raiseHandler.Remove(n.item); + } + + n = n.next; + } + } +#else + ViewHandler viewHandler = new ViewHandler(this); + int index = 0, removed = 0, myoffset = Offset; + Node node = startsentinel.next; + while (node != endsentinel) + { + //pass by a stretch of nodes + while (node != endsentinel && !predicate(node.item)) + { + updatecheck(); + node = node.next; + index++; + } + updatecheck(); + viewHandler.skipEndpoints(removed, myoffset + index); + //Remove a stretch of nodes + Node localend = node.prev; //Latest node not to be removed + while (node != endsentinel && predicate(node.item)) + { + updatecheck(); + if (mustFire) + raiseHandler.Remove(node.item); + removed++; + node = node.next; + index++; + viewHandler.updateViewSizesAndCounts(removed, myoffset + index); + } + updatecheck(); + viewHandler.updateSentinels(myoffset + index, localend, node); + localend.next = node; + node.prev = localend; + } + index = underlying != null ? underlying.size + 1 - myoffset : size + 1 - myoffset; + viewHandler.updateViewSizesAndCounts(removed, myoffset + index); + size -= removed; + if (underlying != null) + underlying.size -= removed; +#endif + raiseHandler.Raise(); + } + + /// <summary> + /// Remove all items from this collection. + /// </summary> + [Tested] + public virtual void Clear() + { + updatecheck(); + if (size == 0) + return; + int oldsize = size; +#if HASHINDEX + if (underlying == null) + dict.Clear(); + else + foreach (T item in this) + dict.Remove(item); +#endif + clear(); + (underlying ?? this).raiseForRemoveInterval(Offset, oldsize); + } + + void clear() + { + if (size == 0) + return; +#if HASHINDEX + //TODO: mix with tag maintenance to only run through list once? + ViewHandler viewHandler = new ViewHandler(this); + if (viewHandler.viewCount > 0) + { + int removed = 0; + Node n = startsentinel.next; + viewHandler.skipEndpoints(0, n); + while (n != endsentinel) + { + removed++; + n = n.next; + viewHandler.updateViewSizesAndCounts(removed, n); + } + viewHandler.updateSentinels(endsentinel, startsentinel, endsentinel); + if (underlying != null) + viewHandler.updateViewSizesAndCounts(removed, underlying.endsentinel); + } +#else + fixViewsBeforeRemove(Offset, size, startsentinel.next, endsentinel.prev); +#endif +#if HASHINDEX + if (underlying != null) + { + Node n = startsentinel.next; + + while (n != endsentinel) + { + n.next.prev = startsentinel; + startsentinel.next = n.next; + removefromtaggroup(n); + n = n.next; + } + } + else + taggroups = 0; +#endif + endsentinel.prev = startsentinel; + startsentinel.next = endsentinel; + if (underlying != null) + underlying.size -= size; + size = 0; + } + + /// <summary> + /// Remove all items not in some other collection from this one, taking multiplicities into account. + /// <para>The asymptotic running time complexity of this method is <code>O(n+m+v*log(v))</code>, + /// where <code>n</code> is the size of this collection, <code>m</code> is the size of the + /// <code>items</code> collection and <code>v</code> is the number of views. + /// The method will temporarily allocate memory of size <code>O(m+v)</code>. The stated complexitiy + /// holds under the assumption that the itemequalityComparer of this list is well-behaved. + /// </para> + /// </summary> + /// <typeparam name="U"></typeparam> + /// <param name="items">The items to retain.</param> + [Tested] + public virtual void RetainAll<U>(SCG.IEnumerable<U> items) where U : T + { + updatecheck(); + if (size == 0) + return; + RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this); + bool mustFire = raiseHandler.MustFire; +#if HASHINDEX + /*if (underlying == null) + { + HashDictionary<T, Node> newdict = new HashDictionary<T, Node>(itemequalityComparer); + foreach (T item in items) + { + Node node; + + if (dict.Remove(item, out node)) + newdict.Add(item, node); + } + foreach (KeyValuePair<T, Node> pair in dict) + { + Node n = pair.Value; + fixViewsBeforeSingleRemove(n, 117); + Node p = n.prev, s = n.next; s.prev = p; p.next = s; + removefromtaggroup(n); + } + dict = newdict; + size = dict.Count; + //For a small number of items to retain it might be faster to + //iterate through the list and splice out the chunks not needed + } + else*/ + { + HashSet<T> toremove = new HashSet<T>(itemequalityComparer); + + foreach (T item in this) + toremove.Add(item); + + foreach (T item in items) + toremove.Remove(item); + + Node n = startsentinel.next; + + while (n != endsentinel && toremove.Count > 0) + { + if (toremove.Contains(n.item)) + { + dict.Remove(n.item); + remove(n, 119); + if (mustFire) + raiseHandler.Remove(n.item); + } + + n = n.next; + } + } +#else + HashBag<T> toretain = new HashBag<T>(itemequalityComparer); + toretain.AddAll(items); + ViewHandler viewHandler = new ViewHandler(this); + int index = 0, removed = 0, myoffset = Offset; + Node node = startsentinel.next; + while (node != endsentinel) + { + //Skip a stretch of nodes + while (node != endsentinel && toretain.Remove(node.item)) + { + node = node.next; + index++; + } + viewHandler.skipEndpoints(removed, myoffset + index); + //Remove a stretch of nodes + Node localend = node.prev; //Latest node not to be removed + while (node != endsentinel && !toretain.Contains(node.item)) + { + if (mustFire) + raiseHandler.Remove(node.item); + removed++; + node = node.next; + index++; + viewHandler.updateViewSizesAndCounts(removed, myoffset + index); + } + viewHandler.updateSentinels(myoffset + index, localend, node); + localend.next = node; + node.prev = localend; + } + index = underlying != null ? underlying.size + 1 - myoffset : size + 1 - myoffset; + viewHandler.updateViewSizesAndCounts(removed, myoffset + index); + size -= removed; + if (underlying != null) + underlying.size -= removed; +#endif + raiseHandler.Raise(); + } + + /// <summary> + /// + /// </summary> + /// <param name="predicate"></param> + void RetainAll(Fun<T, bool> predicate) + { + updatecheck(); + if (size == 0) + return; + RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this); + bool mustFire = raiseHandler.MustFire; +#if HASHINDEX + { + Node n = startsentinel.next; + + while (n != endsentinel) + { + bool removeIt = !predicate(n.item); + updatecheck(); + if (removeIt) + { + dict.Remove(n.item); + remove(n, 119); + if (mustFire) + raiseHandler.Remove(n.item); + } + + n = n.next; + } + } +#else + ViewHandler viewHandler = new ViewHandler(this); + int index = 0, removed = 0, myoffset = Offset; + Node node = startsentinel.next; + while (node != endsentinel) + { + //Skip a stretch of nodes + while (node != endsentinel && predicate(node.item)) + { + updatecheck(); + node = node.next; + index++; + } + updatecheck(); + viewHandler.skipEndpoints(removed, myoffset + index); + //Remove a stretch of nodes + Node localend = node.prev; //Latest node not to be removed + while (node != endsentinel && !predicate(node.item)) + { + updatecheck(); + if (mustFire) + raiseHandler.Remove(node.item); + removed++; + node = node.next; + index++; + viewHandler.updateViewSizesAndCounts(removed, myoffset + index); + } + updatecheck(); + viewHandler.updateSentinels(myoffset + index, localend, node); + localend.next = node; + node.prev = localend; + } + index = underlying != null ? underlying.size + 1 - myoffset : size + 1 - myoffset; + viewHandler.updateViewSizesAndCounts(removed, myoffset + index); + size -= removed; + if (underlying != null) + underlying.size -= removed; +#endif + raiseHandler.Raise(); + } + + /// <summary> + /// Check if this collection contains all the values in another collection + /// with respect to multiplicities. + /// </summary> + /// <param name="items">The </param> + /// <typeparam name="U"></typeparam> + /// <returns>True if all values in <code>items</code>is in this collection.</returns> + [Tested] + public virtual bool ContainsAll<U>(SCG.IEnumerable<U> items) where U : T + { + validitycheck(); +#if HASHINDEX + Node node; + foreach (T item in items) + if (!contains(item, out node)) + return false; + return true; +#else + HashBag<T> tocheck = new HashBag<T>(itemequalityComparer); + tocheck.AddAll(items); + if (tocheck.Count > size) + return false; + Node node = startsentinel.next; + while (node != endsentinel) + { + tocheck.Remove(node.item); + node = node.next; + } + return tocheck.IsEmpty; +#endif + } + + + /// <summary> + /// Create a new list consisting of the items of this list satisfying a + /// certain predicate. + /// </summary> + /// <param name="filter">The filter delegate defining the predicate.</param> + /// <returns>The new list.</returns> + [Tested] + public IList<T> FindAll(Fun<T, bool> filter) + { + validitycheck(); + int stamp = this.stamp; + HashedLinkedList<T> retval = new HashedLinkedList<T>(); + Node cursor = startsentinel.next; + Node mcursor = retval.startsentinel; +#if HASHINDEX + double tagdelta = int.MaxValue / (size + 1.0); + int count = 1; + TagGroup taggroup = new TagGroup(); + retval.taggroups = 1; +#endif + while (cursor != endsentinel) + { + bool found = filter(cursor.item); + modifycheck(stamp); + if (found) + { + mcursor.next = new Node(cursor.item, mcursor, null); + mcursor = mcursor.next; + retval.size++; +#if HASHINDEX + retval.dict.Add(cursor.item, mcursor); + mcursor.taggroup = taggroup; + mcursor.tag = (int)(tagdelta * count++); +#endif + } + cursor = cursor.next; + } +#if HASHINDEX + if (retval.size > 0) + { + taggroup.count = retval.size; + taggroup.first = retval.startsentinel.next; + taggroup.last = mcursor; + } +#endif + retval.endsentinel.prev = mcursor; + mcursor.next = retval.endsentinel; + return retval; + } + + + /// <summary> + /// Count the number of items of the collection equal to a particular value. + /// Returns 0 if and only if the value is not in the collection. + /// </summary> + /// <param name="item">The value to count.</param> + /// <returns>The number of copies found.</returns> + [Tested] + public virtual int ContainsCount(T item) + { +#if HASHINDEX + return Contains(item) ? 1 : 0; +#else + validitycheck(); + int retval = 0; + Node node = startsentinel.next; + while (node != endsentinel) + { + if (itemequalityComparer.Equals(node.item, item)) + retval++; + node = node.next; + } + return retval; +#endif + } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public virtual ICollectionValue<T> UniqueItems() + { +#if HASHINDEX + return this; +#else + HashBag<T> hashbag = new HashBag<T>(itemequalityComparer); + hashbag.AddAll(this); + return hashbag.UniqueItems(); +#endif + } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public virtual ICollectionValue<KeyValuePair<T, int>> ItemMultiplicities() + { +#if HASHINDEX + return new MultiplicityOne<T>(this); +#else + HashBag<T> hashbag = new HashBag<T>(itemequalityComparer); + hashbag.AddAll(this); + return hashbag.ItemMultiplicities(); +#endif + } + + /// <summary> + /// Remove all items equivalent to a given value. + /// <para>The asymptotic complexity of this method is <code>O(n+v*log(v))</code>, + /// where <code>n</code> is the size of the collection and <code>v</code> + /// is the number of views. + /// </para> + /// </summary> + /// <param name="item">The value to remove.</param> + [Tested] + public virtual void RemoveAllCopies(T item) + { +#if HASHINDEX + Remove(item); +#else + updatecheck(); + if (size == 0) + return; + RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this); + bool mustFire = raiseHandler.MustFire; + ViewHandler viewHandler = new ViewHandler(this); + int index = 0, removed = 0, myoffset = Offset; + // + Node node = startsentinel.next; + while (node != endsentinel) + { + //pass by a stretch of nodes + while (node != endsentinel && !itemequalityComparer.Equals(node.item, item)) + { + node = node.next; + index++; + } + viewHandler.skipEndpoints(removed, myoffset + index); + //Remove a stretch of nodes + Node localend = node.prev; //Latest node not to be removed + while (node != endsentinel && itemequalityComparer.Equals(node.item, item)) + { + if (mustFire) + raiseHandler.Remove(node.item); + removed++; + node = node.next; + index++; + viewHandler.updateViewSizesAndCounts(removed, myoffset + index); + } + viewHandler.updateSentinels(myoffset + index, localend, node); + localend.next = node; + node.prev = localend; + } + index = underlying != null ? underlying.size + 1 - myoffset : size + 1 - myoffset; + viewHandler.updateViewSizesAndCounts(removed, myoffset + index); + size -= removed; + if (underlying != null) + underlying.size -= removed; + raiseHandler.Raise(); +#endif + } + + #endregion + + #region ICollectionValue<T> Members + + /// <summary> + /// + /// </summary> + /// <value>The number of items in this collection</value> + [Tested] + public override int Count { [Tested]get { validitycheck(); return size; } } + + /// <summary> + /// Choose some item of this collection. + /// </summary> + /// <exception cref="NoSuchItemException">if collection is empty.</exception> + /// <returns></returns> + [Tested] + public override T Choose() { return First; } + + /// <summary> + /// Create an enumerable, enumerating the items of this collection that satisfies + /// a certain condition. + /// </summary> + /// <param name="filter">The T->bool filter delegate defining the condition</param> + /// <returns>The filtered enumerable</returns> + public override SCG.IEnumerable<T> Filter(Fun<T, bool> filter) { validitycheck(); return base.Filter(filter); } + + #endregion + + #region IEnumerable<T> Members + /// <summary> + /// Create an enumerator for the collection + /// </summary> + /// <returns>The enumerator</returns> + [Tested] + public override SCG.IEnumerator<T> GetEnumerator() + { + validitycheck(); + Node cursor = startsentinel.next; + int enumeratorstamp = underlying != null ? underlying.stamp : this.stamp; + + while (cursor != endsentinel) + { + modifycheck(enumeratorstamp); + yield return cursor.item; + cursor = cursor.next; + } + } + + #endregion + + #region IExtensible<T> Members + /// <summary> + /// Add an item to this collection if possible. + /// </summary> + /// <param name="item">The item to add.</param> + /// <returns>True.</returns> + [Tested] + public virtual bool Add(T item) + { + updatecheck(); +#if HASHINDEX + Node node = new Node(item); + if (!dict.FindOrAdd(item, ref node)) + { + insertNode(true, endsentinel, node); + (underlying ?? this).raiseForAdd(item); + return true; + } + return false; +#else + insert(size, endsentinel, item); + (underlying ?? this).raiseForAdd(item); + return true; +#endif + } + + /// <summary> + /// + /// </summary> + /// <value>True since this collection has bag semantics.</value> + [Tested] + public virtual bool AllowsDuplicates + { + [Tested] + get + { +#if HASHINDEX + return false; +#else + return true; +#endif + } + } + + /// <summary> + /// By convention this is true for any collection with set semantics. + /// </summary> + /// <value>True if only one representative of a group of equal items + /// is kept in the collection together with the total count.</value> + public virtual bool DuplicatesByCounting + { + get + { +#if HASHINDEX + return true; +#else + return false; +#endif + } + } + + /// <summary> + /// Add the elements from another collection with a more specialized item type + /// to this collection. + /// </summary> + /// <typeparam name="U">The type of items to add</typeparam> + /// <param name="items">The items to add</param> + [Tested] + public virtual void AddAll<U>(SCG.IEnumerable<U> items) where U : T + { +#if HASHINDEX + updatecheck(); + int added = 0; + Node pred = endsentinel.prev; + foreach (U item in items) + { + Node node = new Node(item); + if (!dict.FindOrAdd(item, ref node)) + { + insertNode(false, endsentinel, node); + added++; + } + } + if (added > 0) + { + fixViewsAfterInsert(endsentinel, pred, added, 0); + raiseForInsertAll(pred, size - added, added, false); + } +#else + insertAll(size, items, false); +#endif + } + + #endregion + +#if HASHINDEX +#else + #region IStack<T> Members + + /// <summary> + /// Push an item to the top of the stack. + /// </summary> + /// <param name="item">The item</param> + [Tested] + public void Push(T item) + { + InsertLast(item); + } + + /// <summary> + /// Pop the item at the top of the stack from the stack. + /// </summary> + /// <returns>The popped item.</returns> + [Tested] + public T Pop() + { + return RemoveLast(); + } + + #endregion + + #region IQueue<T> Members + + /// <summary> + /// Enqueue an item at the back of the queue. + /// </summary> + /// <param name="item">The item</param> + [Tested] + public virtual void Enqueue(T item) + { + InsertLast(item); + } + + /// <summary> + /// Dequeue an item from the front of the queue. + /// </summary> + /// <returns>The item</returns> + [Tested] + public virtual T Dequeue() + { + return RemoveFirst(); + } + #endregion +#endif + + #region Diagnostic + + private bool checkViews() + { + if (underlying != null) + throw new InternalException(System.Reflection.MethodInfo.GetCurrentMethod() + " called on a view"); + if (views == null) + return true; + bool retval = true; + + Node[] nodes = new Node[size + 2]; + int i = 0; + Node n = startsentinel; + while (n != null) + { + nodes[i++] = n; + n = n.next; + } + //Console.WriteLine("###"); + foreach (HashedLinkedList<T> view in views) + { + if (!view.isValid) + { + Console.WriteLine("Invalid view(hash {0}, offset {1}, size {2})", + view.GetHashCode(), view.offset, view.size); + retval = false; + continue; + } + if (view.Offset > size || view.Offset < 0) + { + Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), Offset > underlying.size ({2})", + view.GetHashCode(), view.offset, view.size, size); + retval = false; + } + else if (view.startsentinel != nodes[view.Offset]) + { + Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), startsentinel {3} should be {4}", + view.GetHashCode(), view.offset, view.size, + view.startsentinel + " " + view.startsentinel.GetHashCode(), + nodes[view.Offset] + " " + nodes[view.Offset].GetHashCode()); + retval = false; + } + if (view.Offset + view.size > size || view.Offset + view.size < 0) + { + Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), end index > underlying.size ({3})", + view.GetHashCode(), view.offset, view.size, size); + retval = false; + } + else if (view.endsentinel != nodes[view.Offset + view.size + 1]) + { + Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), endsentinel {3} should be {4}", + view.GetHashCode(), view.offset, view.size, + view.endsentinel + " " + view.endsentinel.GetHashCode(), + nodes[view.Offset + view.size + 1] + " " + nodes[view.Offset + view.size + 1].GetHashCode()); + retval = false; + } + if (view.views != views) + { + Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), wrong views list {3} <> {4}", + view.GetHashCode(), view.offset, view.size, view.views.GetHashCode(), views.GetHashCode()); + retval = false; + } + if (view.underlying != this) + { + Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), wrong underlying {3} <> this {4}", + view.GetHashCode(), view.offset, view.size, view.underlying.GetHashCode(), GetHashCode()); + retval = false; + } + if (view.stamp != stamp) + { + //Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), wrong stamp view:{2} underlying: {3}", view.GetHashCode(),view.offset, view.size, view.stamp, stamp); + //retval = false; + } + } + return retval; + } + + string zeitem(Node node) + { + return node == null ? "(null node)" : node.item.ToString(); + } + + /// <summary> + /// Check the sanity of this list + /// </summary> + /// <returns>true if sane</returns> + [Tested] + public virtual bool Check() + { + bool retval = true; + + /*if (underlying != null && underlying.stamp != stamp) + { + Console.WriteLine("underlying != null && underlying.stamp({0}) != stamp({1})", underlying.stamp, stamp); + retval = false; + }*/ + + if (underlying != null) + { + //TODO: check that this view is included in viewsEndpoints tree + return underlying.Check(); + } + + if (startsentinel == null) + { + Console.WriteLine("startsentinel == null"); + retval = false; + } + + if (endsentinel == null) + { + Console.WriteLine("endsentinel == null"); + retval = false; + } + + if (size == 0) + { + if (startsentinel != null && startsentinel.next != endsentinel) + { + Console.WriteLine("size == 0 but startsentinel.next != endsentinel"); + retval = false; + } + + if (endsentinel != null && endsentinel.prev != startsentinel) + { + Console.WriteLine("size == 0 but endsentinel.prev != startsentinel"); + retval = false; + } + } + + if (startsentinel == null) + { + Console.WriteLine("NULL startsentinel"); + return retval; + } + + int count = 0; + Node node = startsentinel.next, prev = startsentinel; +#if HASHINDEX + int taggroupsize = 0, oldtaggroupsize = losize + 1, seentaggroups = 0; + TagGroup oldtg = null; + + if (underlying == null) + { + TagGroup tg = startsentinel.taggroup; + + if (tg.count != 0 || tg.first != null || tg.last != null || tg.tag != int.MinValue) + { + Console.WriteLine("Bad startsentinel tag group: {0}", tg); + retval = false; + } + + tg = endsentinel.taggroup; + if (tg.count != 0 || tg.first != null || tg.last != null || tg.tag != int.MaxValue) + { + Console.WriteLine("Bad endsentinel tag group: {0}", tg); + retval = false; + } + } +#endif + while (node != endsentinel) + { + count++; + if (node.prev != prev) + { + Console.WriteLine("Bad backpointer at node {0}", count); + retval = false; + } +#if HASHINDEX + if (underlying == null) + { + if (!node.prev.precedes(node)) + { + Console.WriteLine("node.prev.tag ({0}, {1}) >= node.tag ({2}, {3}) at index={4} item={5} ", node.prev.taggroup.tag, node.prev.tag, node.taggroup.tag, node.tag, count, node.item); + retval = false; + } + + if (node.taggroup != oldtg) + { + + if (node.taggroup.first != node) + { + string ntfi = zeitem(node.taggroup.first); + Console.WriteLine("Bad first pointer in taggroup: node.taggroup.first.item ({0}), node.item ({1}) at index={2} item={3}", ntfi, node.item, count, node.item); + retval = false; + } + + if (oldtg != null) + { + if (oldtg.count != taggroupsize) + { + Console.WriteLine("Bad taggroupsize: oldtg.count ({0}) != taggroupsize ({1}) at index={2} item={3}", oldtg.count, taggroupsize, count, node.item); + retval = false; + } + + if (oldtaggroupsize <= losize && taggroupsize <= losize) + { + Console.WriteLine("Two small taggroups in a row: oldtaggroupsize ({0}), taggroupsize ({1}) at index={2} item={3}", oldtaggroupsize, taggroupsize, count, node.item); + retval = false; + } + + if (node.taggroup.tag <= oldtg.tag) + { + Console.WriteLine("Taggroup tags not strictly increasing: oldtaggrouptag ({0}), taggrouptag ({1}) at index={2} item={3}", oldtg.tag, node.taggroup.tag, count, node.item); + retval = false; + } + + if (oldtg.last != node.prev) + { + Console.WriteLine("Bad last pointer in taggroup: oldtg.last.item ({0}), node.prev.item ({1}) at index={2} item={3}", oldtg.last.item, node.prev.item, count, node.item); + retval = false; + } + + oldtaggroupsize = taggroupsize; + } + + seentaggroups++; + oldtg = node.taggroup; + taggroupsize = 1; + } + else + { + taggroupsize++; + } + } + +#endif + prev = node; + node = node.next; + if (node == null) + { + Console.WriteLine("Null next pointer at node {0}", count); + return false; + } + } + +#if HASHINDEX + if (underlying == null && size == 0 && taggroups != 0) + { + Console.WriteLine("Bad taggroups for empty list: size={0} taggroups={1}", size, taggroups); + retval = false; + } + if (underlying == null && size > 0) + { + oldtg = node.prev.taggroup; + if (oldtg != null) + { + if (oldtg.count != taggroupsize) + { + Console.WriteLine("Bad taggroupsize: oldtg.count ({0}) != taggroupsize ({1}) at index={2} item={3}", oldtg.count, taggroupsize, count, node.item); + retval = false; + } + + if (oldtaggroupsize <= losize && taggroupsize <= losize) + { + Console.WriteLine("Two small taggroups in a row: oldtaggroupsize ({0}), taggroupsize ({1}) at index={2} item={3}", oldtaggroupsize, taggroupsize, count, node.item); + retval = false; + } + + if (node.taggroup.tag <= oldtg.tag) + { + Console.WriteLine("Taggroup tags not strictly increasing: oldtaggrouptag ({0}), taggrouptag ({1}) at index={2} item={3}", oldtg.tag, node.taggroup.tag, count, node.item); + retval = false; + } + + if (oldtg.last != node.prev) + { + Console.WriteLine("Bad last pointer in taggroup: oldtg.last.item ({0}), node.prev.item ({1}) at index={2} item={3}", zeitem(oldtg.last), zeitem(node.prev), count, node.item); + retval = false; + } + } + + if (seentaggroups != taggroups) + { + Console.WriteLine("seentaggroups ({0}) != taggroups ({1}) (at size {2})", seentaggroups, taggroups, size); + retval = false; + } + } +#endif + if (count != size) + { + Console.WriteLine("size={0} but enumeration gives {1} nodes ", size, count); + retval = false; + } + + retval = checkViews() && retval; + +#if HASHINDEX + if (!retval) + return false; + if (underlying == null) + { + if (size != dict.Count) + { + Console.WriteLine("list.size ({0}) != dict.Count ({1})", size, dict.Count); + retval = false; + } + Node n = startsentinel.next, n2; + while (n != endsentinel) + { + if (!dict.Find(n.item, out n2)) + { + Console.WriteLine("Item in list but not dict: {0}", n.item); + retval = false; + } + else if (n != n2) + { + Console.WriteLine("Wrong node in dict for item: {0}", n.item); + retval = false; + } + n = n.next; + } + } +#endif + return retval; + } + #endregion + + #region ICloneable Members + + /// <summary> + /// Make a shallow copy of this HashedLinkedList. + /// </summary> + /// <returns></returns> + public virtual object Clone() + { + HashedLinkedList<T> clone = new HashedLinkedList<T>(itemequalityComparer); + clone.AddAll(this); + return clone; + } + + #endregion + + #region System.Collections.Generic.IList<T> Members + + void System.Collections.Generic.IList<T>.RemoveAt(int index) + { + RemoveAt(index); + } + + void System.Collections.Generic.ICollection<T>.Add(T item) + { + Add(item); + } + + #endregion + + #region System.Collections.ICollection Members + + bool System.Collections.ICollection.IsSynchronized + { + get { return false; } + } + + [Obsolete] + Object System.Collections.ICollection.SyncRoot + { + // Presumably safe to use the startsentinel (of type Node, always != null) as SyncRoot + // since the class Node is private. + get { return underlying != null ? ((System.Collections.ICollection)underlying).SyncRoot : startsentinel; } + } + + void System.Collections.ICollection.CopyTo(Array arr, int index) + { + if (index < 0 || index + Count > arr.Length) + throw new ArgumentOutOfRangeException(); + + foreach (T item in this) + arr.SetValue(item, index++); + } + + #endregion + + #region System.Collections.IList Members + + Object System.Collections.IList.this[int index] + { + get { return this[index]; } + set { this[index] = (T)value; } + } + + int System.Collections.IList.Add(Object o) + { + bool added = Add((T)o); + // What position to report if item not added? SC.IList.Add doesn't say + return added ? Count-1 : -1; + } + + bool System.Collections.IList.Contains(Object o) + { + return Contains((T)o); + } + + int System.Collections.IList.IndexOf(Object o) + { + return Math.Max(-1, IndexOf((T)o)); + } + + void System.Collections.IList.Insert(int index, Object o) + { + Insert(index, (T)o); + } + + void System.Collections.IList.Remove(Object o) + { + Remove((T)o); + } + + void System.Collections.IList.RemoveAt(int index) + { + RemoveAt(index); + } + + #endregion + } +} diff --git a/mcs/class/Mono.C5/C5/linkedlists/LinkedList.cs b/mcs/class/Mono.C5/C5/linkedlists/LinkedList.cs index 71350d4a259..9c672ff0d83 100644 --- a/mcs/class/Mono.C5/C5/linkedlists/LinkedList.cs +++ b/mcs/class/Mono.C5/C5/linkedlists/LinkedList.cs @@ -1,3919 +1,3919 @@ -/*
- Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-
-#define HASHINDEXnot
-
-using System;
-using System.Diagnostics;
-using SCG = System.Collections.Generic;
-
-namespace C5
-{
- /// <summary>
- /// A list collection class based on a doubly linked list data structure.
- /// </summary>
- [Serializable]
- public class LinkedList<T> : SequencedBase<T>, IList<T>, SCG.IList<T>
-#if HASHINDEX
-#else
-, IStack<T>, IQueue<T>
-#endif
- {
- #region Fields
- /// <summary>
- /// IExtensible.Add(T) always does AddLast(T), fIFO determines
- /// if T Remove() does RemoveFirst() or RemoveLast()
- /// </summary>
- bool fIFO = true;
-
- #region Events
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public override EventTypeEnum ListenableEvents { get { return underlying == null ? EventTypeEnum.All : EventTypeEnum.None; } }
-
- #endregion
-
- //Invariant: startsentinel != null && endsentinel != null
- //If size==0: startsentinel.next == endsentinel && endsentinel.prev == startsentinel
- //Else: startsentinel.next == First && endsentinel.prev == Last)
- /// <summary>
- /// Node to the left of first node
- /// </summary>
- Node startsentinel;
- /// <summary>
- /// Node to the right of last node
- /// </summary>
- Node endsentinel;
- /// <summary>
- /// Offset of this view in underlying list
- /// </summary>
-#if HASHINDEX
- int? offset;
-#else
- int offset;
-#endif
-
- /// <summary>
- /// underlying list of this view (or null for the underlying list)
- /// </summary>
- LinkedList<T> underlying;
-
- //Note: all views will have the same views list since all view objects are created by MemberwiseClone()
- WeakViewList<LinkedList<T>> views;
- WeakViewList<LinkedList<T>>.Node myWeakReference;
-
- /// <summary>
- /// Has this list or view not been invalidated by some operation (by someone calling Dispose())
- /// </summary>
- bool isValid = true;
-
-
-#if HASHINDEX
- HashDictionary<T, Node> dict;
- /// <summary>
- /// Number of taggroups
- /// </summary>
- int taggroups;
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- int Taggroups
- {
- get { return underlying == null ? taggroups : underlying.taggroups; }
- set { if (underlying == null) taggroups = value; else underlying.taggroups = value; }
- }
-#endif
-
- #endregion
-
- #region Util
-
- bool equals(T i1, T i2) { return itemequalityComparer.Equals(i1, i2); }
-
- #region Check utilities
- /// <summary>
- /// Check if it is valid to perform updates and increment stamp of
- /// underlying if this is a view.
- /// <para>This method should be called in every public modifying
- /// methods before any modifications are performed.
- /// </para>
- /// </summary>
- /// <exception cref="InvalidOperationException"> if check fails.</exception>
- protected override void updatecheck()
- {
- validitycheck();
- base.updatecheck();
- if (underlying != null)
- underlying.stamp++;
- }
-
- /// <summary>
- /// Check if we are a view that the underlyinglist has only been updated through us.
- /// <br/>
- /// This method should be called from enumerators etc to guard against
- /// modification of the base collection.
- /// </summary>
- /// <exception cref="InvalidOperationException"> if check fails.</exception>
- void validitycheck()
- {
- if (!isValid)
- throw new ViewDisposedException();
- }
-
- /// <summary>
- /// Check that the list has not been updated since a particular time.
- /// </summary>
- /// <param name="stamp">The stamp indicating the time.</param>
- /// <exception cref="CollectionModifiedException"> if check fails.</exception>
- protected override void modifycheck(int stamp)
- {
- validitycheck();
- if ((underlying != null ? underlying.stamp : this.stamp) != stamp)
- throw new CollectionModifiedException();
- }
- #endregion
-
- #region Searching
- bool contains(T item, out Node node)
- {
-#if HASHINDEX
- if (dict.Find(item, out node))
- return insideview(node);
-#else
- //TODO: search from both ends? Or search from the end selected by FIFO?
- node = startsentinel.next;
- while (node != endsentinel)
- {
- if (equals(item, node.item))
- return true;
- node = node.next;
- }
-#endif
- return false;
- }
-
- /// <summary>
- /// Search forwards from a node for a node with a particular item.
- /// </summary>
- /// <param name="item">The item to look for</param>
- /// <param name="node">On input, the node to start at. If item was found, the node found on output.</param>
- /// <param name="index">If node was found, the value will be the number of links followed higher than
- /// the value on input. If item was not found, the value on output is undefined.</param>
- /// <returns>True if node was found.</returns>
- bool find(T item, ref Node node, ref int index)
- {
- while (node != endsentinel)
- {
- //if (item.Equals(node.item))
- if (itemequalityComparer.Equals(item, node.item))
- return true;
-
- index++;
- node = node.next;
- }
-
- return false;
- }
-
- bool dnif(T item, ref Node node, ref int index)
- {
- while (node != startsentinel)
- {
- //if (item.Equals(node.item))
- if (itemequalityComparer.Equals(item, node.item))
- return true;
-
- index--;
- node = node.prev;
- }
-
- return false;
- }
-
-#if HASHINDEX
- bool insideview(Node node)
- {
- if (underlying == null)
- return true;
- return (startsentinel.precedes(node) && node.precedes(endsentinel));
- }
-#endif
-
- #endregion
-
- #region Indexing
- /// <summary>
- /// Return the node at position pos
- /// </summary>
- /// <param name="pos"></param>
- /// <returns></returns>
- Node get(int pos)
- {
- if (pos < 0 || pos >= size)
- throw new IndexOutOfRangeException();
- else if (pos < size / 2)
- { // Closer to front
- Node node = startsentinel;
-
- for (int i = 0; i <= pos; i++)
- node = node.next;
-
- return node;
- }
- else
- { // Closer to end
- Node node = endsentinel;
-
- for (int i = size; i > pos; i--)
- node = node.prev;
-
- return node;
- }
- }
-
- /// <summary>
- /// Find the distance from pos to the set given by positions. Return the
- /// signed distance as return value and as an out parameter, the
- /// array index of the nearest position. This is used for up to length 5 of
- /// positions, and we do not assume it is sorted.
- /// </summary>
- /// <param name="pos"></param>
- /// <param name="positions"></param>
- /// <param name="nearest"></param>
- /// <returns></returns>
- int dist(int pos, out int nearest, int[] positions)
- {
- nearest = -1;
- int bestdist = int.MaxValue;
- int signeddist = bestdist;
- for (int i = 0; i < positions.Length; i++)
- {
- int thisdist = positions[i] - pos;
- if (thisdist >= 0 && thisdist < bestdist) { nearest = i; bestdist = thisdist; signeddist = thisdist; }
- if (thisdist < 0 && -thisdist < bestdist) { nearest = i; bestdist = -thisdist; signeddist = thisdist; }
- }
- return signeddist;
- }
-
- /// <summary>
- /// Find the node at position pos, given known positions of several nodes.
- /// </summary>
- /// <param name="pos"></param>
- /// <param name="positions"></param>
- /// <param name="nodes"></param>
- /// <returns></returns>
- Node get(int pos, int[] positions, Node[] nodes)
- {
- int nearest;
- int delta = dist(pos, out nearest, positions);
- Node node = nodes[nearest];
- if (delta > 0)
- for (int i = 0; i < delta; i++)
- node = node.prev;
- else
- for (int i = 0; i > delta; i--)
- node = node.next;
- return node;
- }
-
- /// <summary>
- /// Get nodes at positions p1 and p2, given nodes at several positions.
- /// </summary>
- /// <param name="p1"></param>
- /// <param name="p2"></param>
- /// <param name="n1"></param>
- /// <param name="n2"></param>
- /// <param name="positions"></param>
- /// <param name="nodes"></param>
- void getPair(int p1, int p2, out Node n1, out Node n2, int[] positions, Node[] nodes)
- {
- int nearest1, nearest2;
- int delta1 = dist(p1, out nearest1, positions), d1 = delta1 < 0 ? -delta1 : delta1;
- int delta2 = dist(p2, out nearest2, positions), d2 = delta2 < 0 ? -delta2 : delta2;
-
- if (d1 < d2)
- {
- n1 = get(p1, positions, nodes);
- n2 = get(p2, new int[] { positions[nearest2], p1 }, new Node[] { nodes[nearest2], n1 });
- }
- else
- {
- n2 = get(p2, positions, nodes);
- n1 = get(p1, new int[] { positions[nearest1], p2 }, new Node[] { nodes[nearest1], n2 });
- }
- }
- #endregion
-
- #region Insertion
-#if HASHINDEX
- void insert(int index, Node succ, T item)
- {
- Node newnode = new Node(item);
- if (dict.FindOrAdd(item, ref newnode))
- throw new DuplicateNotAllowedException("Item already in indexed list");
- insertNode(true, succ, newnode);
- }
-
- /// <summary>
- /// Insert a Node before another one. Unchecked version.
- /// </summary>
- /// <param name="succ">The successor to be</param>
- /// <param name="newnode">Node to insert</param>
- /// <param name="updateViews">update overlapping view in this call</param>
- void insertNode(bool updateViews, Node succ, Node newnode)
- {
- newnode.next = succ;
- Node pred = newnode.prev = succ.prev;
- succ.prev.next = newnode;
- succ.prev = newnode;
- size++;
- if (underlying != null)
- underlying.size++;
- settag(newnode);
- if (updateViews)
- fixViewsAfterInsert(succ, pred, 1, 0);
- }
-#else
- /// <summary>
- ///
- /// </summary>
- /// <param name="index">The index in this view</param>
- /// <param name="succ"></param>
- /// <param name="item"></param>
- /// <returns></returns>
- Node insert(int index, Node succ, T item)
- {
- Node newnode = new Node(item, succ.prev, succ);
- succ.prev.next = newnode;
- succ.prev = newnode;
- size++;
- if (underlying != null)
- underlying.size++;
- fixViewsAfterInsert(succ, newnode.prev, 1, Offset + index);
- return newnode;
- }
-#endif
- #endregion
-
- #region Removal
- T remove(Node node, int index)
- {
- fixViewsBeforeSingleRemove(node, Offset + index);
- node.prev.next = node.next;
- node.next.prev = node.prev;
- size--;
- if (underlying != null)
- underlying.size--;
-#if HASHINDEX
- removefromtaggroup(node);
-#endif
- return node.item;
- }
-
-#if HASHINDEX
- private bool dictremove(T item, out Node node)
- {
- if (underlying == null)
- {
- if (!dict.Remove(item, out node))
- return false;
- }
- else
- {
- //We cannot avoid calling dict twice - have to intersperse the listorder test!
- if (!contains(item, out node))
- return false;
- dict.Remove(item);
- }
- return true;
- }
-#endif
- #endregion
-
- #region fixView utilities
- /// <summary>
- ///
- /// </summary>
- /// <param name="added">The actual number of inserted nodes</param>
- /// <param name="pred">The predecessor of the inserted nodes</param>
- /// <param name="succ">The successor of the added nodes</param>
- /// <param name="realInsertionIndex"></param>
- void fixViewsAfterInsert(Node succ, Node pred, int added, int realInsertionIndex)
- {
- if (views != null)
- foreach (LinkedList<T> view in views)
- {
- if (view != this)
- {
-#if HASHINDEX
- if (pred.precedes(view.startsentinel) || (view.startsentinel == pred && view.size > 0))
- view.offset += added;
- if (view.startsentinel.precedes(pred) && succ.precedes(view.endsentinel))
- view.size += added;
- if (view.startsentinel == pred && view.size > 0)
- view.startsentinel = succ.prev;
- if (view.endsentinel == succ)
- view.endsentinel = pred.next;
-#else
- if (view.Offset == realInsertionIndex && view.size > 0)
- view.startsentinel = succ.prev;
- if (view.Offset + view.size == realInsertionIndex)
- view.endsentinel = pred.next;
- if (view.Offset < realInsertionIndex && view.Offset + view.size > realInsertionIndex)
- view.size += added;
- if (view.Offset > realInsertionIndex || (view.Offset == realInsertionIndex && view.size > 0))
- view.offset += added;
-#endif
- }
- }
- }
-
- void fixViewsBeforeSingleRemove(Node node, int realRemovalIndex)
- {
- if (views != null)
- foreach (LinkedList<T> view in views)
- {
- if (view != this)
- {
-#if HASHINDEX
- if (view.startsentinel.precedes(node) && node.precedes(view.endsentinel))
- view.size--;
- if (!view.startsentinel.precedes(node))
- view.offset--;
- if (view.startsentinel == node)
- view.startsentinel = node.prev;
- if (view.endsentinel == node)
- view.endsentinel = node.next;
-#else
- if (view.offset - 1 == realRemovalIndex)
- view.startsentinel = node.prev;
- if (view.offset + view.size == realRemovalIndex)
- view.endsentinel = node.next;
- if (view.offset <= realRemovalIndex && view.offset + view.size > realRemovalIndex)
- view.size--;
- if (view.offset > realRemovalIndex)
- view.offset--;
-#endif
- }
- }
- }
-
-#if HASHINDEX
-#else
- void fixViewsBeforeRemove(int start, int count, Node first, Node last)
- {
- int clearend = start + count - 1;
- if (views != null)
- foreach (LinkedList<T> view in views)
- {
- if (view == this)
- continue;
- int viewoffset = view.Offset, viewend = viewoffset + view.size - 1;
- //sentinels
- if (start < viewoffset && viewoffset - 1 <= clearend)
- view.startsentinel = first.prev;
- if (start <= viewend + 1 && viewend < clearend)
- view.endsentinel = last.next;
- //offsets and sizes
- if (start < viewoffset)
- {
- if (clearend < viewoffset)
- view.offset = viewoffset - count;
- else
- {
- view.offset = start;
- view.size = clearend < viewend ? viewend - clearend : 0;
- }
- }
- else if (start <= viewend)
- view.size = clearend <= viewend ? view.size - count : start - viewoffset;
- }
- }
-#endif
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="otherView"></param>
- /// <returns>The position of View(otherOffset, otherSize) wrt. this view</returns>
- MutualViewPosition viewPosition(LinkedList<T> otherView)
- {
-#if HASHINDEX
- Node otherstartsentinel = otherView.startsentinel, otherendsentinel = otherView.endsentinel,
- first = startsentinel.next, last = endsentinel.prev,
- otherfirst = otherstartsentinel.next, otherlast = otherendsentinel.prev;
- if (last.precedes(otherfirst) || otherlast.precedes(first))
- return MutualViewPosition.NonOverlapping;
- if (size == 0 || (otherstartsentinel.precedes(first) && last.precedes(otherendsentinel)))
- return MutualViewPosition.Contains;
- if (otherView.size == 0 || (startsentinel.precedes(otherfirst) && otherlast.precedes(endsentinel)))
- return MutualViewPosition.ContainedIn;
- return MutualViewPosition.Overlapping;
-#else
- int end = offset + size, otherOffset = otherView.offset, otherSize = otherView.size, otherEnd = otherOffset + otherSize;
- if (otherOffset >= end || otherEnd <= offset)
- return MutualViewPosition.NonOverlapping;
- if (size == 0 || (otherOffset <= offset && end <= otherEnd))
- return MutualViewPosition.Contains;
- if (otherSize == 0 || (offset <= otherOffset && otherEnd <= end))
- return MutualViewPosition.ContainedIn;
- return MutualViewPosition.Overlapping;
-#endif
- }
-
- void disposeOverlappingViews(bool reverse)
- {
- if (views != null)
- {
- foreach (LinkedList<T> view in views)
- {
- if (view != this)
- {
- switch (viewPosition(view))
- {
- case MutualViewPosition.ContainedIn:
- if (reverse)
- { }
- else
- view.Dispose();
- break;
- case MutualViewPosition.Overlapping:
- view.Dispose();
- break;
- case MutualViewPosition.Contains:
- case MutualViewPosition.NonOverlapping:
- break;
- }
- }
- }
- }
- }
-
- #endregion
-
- #endregion
-
- #region Constructors
-
- /// <summary>
- /// Create a linked list with en external item equalityComparer
- /// </summary>
- /// <param name="itemequalityComparer">The external equalityComparer</param>
- public LinkedList(SCG.IEqualityComparer<T> itemequalityComparer)
- : base(itemequalityComparer)
- {
- offset = 0;
- size = stamp = 0;
- startsentinel = new Node(default(T));
- endsentinel = new Node(default(T));
- startsentinel.next = endsentinel;
- endsentinel.prev = startsentinel;
-#if HASHINDEX
- //It is important that the sentinels are different:
- startsentinel.taggroup = new TagGroup();
- startsentinel.taggroup.tag = int.MinValue;
- startsentinel.taggroup.count = 0;
- endsentinel.taggroup = new TagGroup();
- endsentinel.taggroup.tag = int.MaxValue;
- endsentinel.taggroup.count = 0;
- dict = new HashDictionary<T, Node>(itemequalityComparer);
-#endif
- }
-
- /// <summary>
- /// Create a linked list with the natural item equalityComparer
- /// </summary>
- public LinkedList() : this(EqualityComparer<T>.Default) { }
-
- #endregion
-
- #region Node nested class
-
- /// <summary>
- /// An individual cell in the linked list
- /// </summary>
- [Serializable]
- class Node
- {
- public Node prev;
-
- public Node next;
-
- public T item;
-
- #region Tag support
-#if HASHINDEX
- internal int tag;
-
- internal TagGroup taggroup;
-
- internal bool precedes(Node that)
- {
- //Debug.Assert(taggroup != null, "taggroup field null");
- //Debug.Assert(that.taggroup != null, "that.taggroup field null");
- int t1 = taggroup.tag;
- int t2 = that.taggroup.tag;
-
- return t1 < t2 ? true : t1 > t2 ? false : tag < that.tag;
- }
-#endif
- #endregion
-
- [Tested]
- internal Node(T item) { this.item = item; }
-
- [Tested]
- internal Node(T item, Node prev, Node next)
- {
- this.item = item; this.prev = prev; this.next = next;
- }
-
- public override string ToString()
- {
-#if HASHINDEX
- return String.Format("Node: (item={0}, tag={1})", item, tag);
-#else
- return String.Format("Node(item={0})", item);
-#endif
- }
- }
-
- #endregion
-
- #region Taggroup nested class and tag maintenance utilities
-#if HASHINDEX
- /// <summary>
- /// A group of nodes with the same high tag. Purpose is to be
- /// able to tell the sequence order of two nodes without having to scan through
- /// the list.
- /// </summary>
- [Serializable]
- class TagGroup
- {
- internal int tag, count;
-
- internal Node first, last;
-
- /// <summary>
- /// Pretty print a tag group
- /// </summary>
- /// <returns>Formatted tag group</returns>
- public override string ToString()
- { return String.Format("TagGroup(tag={0}, cnt={1}, fst={2}, lst={3})", tag, count, first, last); }
- }
-
- //Constants for tag maintenance
- const int wordsize = 32;
-
- const int lobits = 3;
-
- const int hibits = lobits + 1;
-
- const int losize = 1 << lobits;
-
- const int hisize = 1 << hibits;
-
- const int logwordsize = 5;
-
- TagGroup gettaggroup(Node pred, Node succ, out int lowbound, out int highbound)
- {
- TagGroup predgroup = pred.taggroup, succgroup = succ.taggroup;
-
- if (predgroup == succgroup)
- {
- lowbound = pred.tag + 1;
- highbound = succ.tag - 1;
- return predgroup;
- }
- else if (predgroup.first != null)
- {
- lowbound = pred.tag + 1;
- highbound = int.MaxValue;
- return predgroup;
- }
- else if (succgroup.first != null)
- {
- lowbound = int.MinValue;
- highbound = succ.tag - 1;
- return succgroup;
- }
- else
- {
- lowbound = int.MinValue;
- highbound = int.MaxValue;
- return new TagGroup();
- }
- }
-
-
- /// <summary>
- /// Put a tag on a node (already inserted in the list). Split taggroups and renumber as
- /// necessary.
- /// </summary>
- /// <param name="node">The node to tag</param>
- void settag(Node node)
- {
- Node pred = node.prev, succ = node.next;
- TagGroup predgroup = pred.taggroup, succgroup = succ.taggroup;
-
- if (predgroup == succgroup)
- {
- node.taggroup = predgroup;
- predgroup.count++;
- if (pred.tag + 1 == succ.tag)
- splittaggroup(predgroup);
- else
- node.tag = (pred.tag + 1) / 2 + (succ.tag - 1) / 2;
- }
- else if (predgroup.first != null)
- {
- node.taggroup = predgroup;
- predgroup.last = node;
- predgroup.count++;
- if (pred.tag == int.MaxValue)
- splittaggroup(predgroup);
- else
- node.tag = pred.tag / 2 + int.MaxValue / 2 + 1;
- }
- else if (succgroup.first != null)
- {
- node.taggroup = succgroup;
- succgroup.first = node;
- succgroup.count++;
- if (succ.tag == int.MinValue)
- splittaggroup(node.taggroup);
- else
- node.tag = int.MinValue / 2 + (succ.tag - 1) / 2;
- }
- else
- {
- Debug.Assert(Taggroups == 0);
-
- TagGroup newgroup = new TagGroup();
-
- Taggroups = 1;
- node.taggroup = newgroup;
- newgroup.first = newgroup.last = node;
- newgroup.count = 1;
- return;
- }
- }
-
-
- /// <summary>
- /// Remove a node from its taggroup.
- /// <br/> When this is called, node must already have been removed from the underlying list
- /// </summary>
- /// <param name="node">The node to remove</param>
- void removefromtaggroup(Node node)
- {
-
- TagGroup taggroup = node.taggroup;
-
- if (--taggroup.count == 0)
- {
- Taggroups--;
- return;
- }
-
- if (node == taggroup.first)
- taggroup.first = node.next;
-
- if (node == taggroup.last)
- taggroup.last = node.prev;
-
- //node.taggroup = null;
- if (taggroup.count != losize || Taggroups == 1)
- return;
-
- TagGroup otg;
- // bug20070911:
- Node neighbor;
- if ((neighbor = taggroup.first.prev) != startsentinel
- && (otg = neighbor.taggroup).count <= losize)
- taggroup.first = otg.first;
- else if ((neighbor = taggroup.last.next) != endsentinel
- && (otg = neighbor.taggroup).count <= losize)
- taggroup.last = otg.last;
- else
- return;
-
- Node n = otg.first;
-
- for (int i = 0, length = otg.count; i < length; i++)
- {
- n.taggroup = taggroup;
- n = n.next;
- }
-
- taggroup.count += otg.count;
- Taggroups--;
- n = taggroup.first;
-
- const int ofs = wordsize - hibits;
-
- for (int i = 0, count = taggroup.count; i < count; i++)
- {
- n.tag = (i - losize) << ofs; //(i-8)<<28
- n = n.next;
- }
- }
-
-
- /// <summary>
- /// Split a tag group to make rom for more tags.
- /// </summary>
- /// <param name="taggroup">The tag group</param>
- void splittaggroup(TagGroup taggroup)
- {
- Node n = taggroup.first;
- int ptgt = taggroup.first.prev.taggroup.tag;
- int ntgt = taggroup.last.next.taggroup.tag;
-
- Debug.Assert(ptgt + 1 <= ntgt - 1);
-
- int ofs = wordsize - hibits;
- int newtgs = (taggroup.count - 1) / hisize;
- int tgtdelta = (int)((ntgt + 0.0 - ptgt) / (newtgs + 2)), tgtag = ptgt;
-
- tgtdelta = tgtdelta == 0 ? 1 : tgtdelta;
- for (int j = 0; j < newtgs; j++)
- {
- TagGroup newtaggroup = new TagGroup();
-
- newtaggroup.tag = (tgtag = tgtag >= ntgt - tgtdelta ? ntgt : tgtag + tgtdelta);
- newtaggroup.first = n;
- newtaggroup.count = hisize;
- for (int i = 0; i < hisize; i++)
- {
- n.taggroup = newtaggroup;
- n.tag = (i - losize) << ofs; //(i-8)<<28
- n = n.next;
- }
-
- newtaggroup.last = n.prev;
- }
-
- int rest = taggroup.count - hisize * newtgs;
-
- taggroup.first = n;
- taggroup.count = rest;
- taggroup.tag = (tgtag = tgtag >= ntgt - tgtdelta ? ntgt : tgtag + tgtdelta); ofs--;
- for (int i = 0; i < rest; i++)
- {
- n.tag = (i - hisize) << ofs; //(i-16)<<27
- n = n.next;
- }
-
- taggroup.last = n.prev;
- Taggroups += newtgs;
- if (tgtag == ntgt)
- redistributetaggroups(taggroup);
- }
-
-
- private void redistributetaggroups(TagGroup taggroup)
- {
- TagGroup pred = taggroup, succ = taggroup, tmp;
- double limit = 1, bigt = Math.Pow(Taggroups, 1.0 / 30);//?????
- int bits = 1, count = 1, lowmask = 0, himask = 0, target = 0;
-
- do
- {
- bits++;
- lowmask = (1 << bits) - 1;
- himask = ~lowmask;
- target = taggroup.tag & himask;
- while ((tmp = pred.first.prev.taggroup).first != null && (tmp.tag & himask) == target)
- { count++; pred = tmp; }
-
- while ((tmp = succ.last.next.taggroup).last != null && (tmp.tag & himask) == target)
- { count++; succ = tmp; }
-
- limit *= bigt;
- } while (count > limit);
-
- //redistibute tags
- int lob = pred.first.prev.taggroup.tag, upb = succ.last.next.taggroup.tag;
- int delta = upb / (count + 1) - lob / (count + 1);
-
- Debug.Assert(delta > 0);
- for (int i = 0; i < count; i++)
- {
- pred.tag = lob + (i + 1) * delta;
- pred = pred.last.next.taggroup;
- }
- }
-#endif
-
- #endregion
-
- #region Position, PositionComparer and ViewHandler nested types
- class PositionComparer : SCG.IComparer<Position>
- {
- static PositionComparer _default;
- PositionComparer() { }
- public static PositionComparer Default { get { return _default ?? (_default = new PositionComparer()); } }
- public int Compare(Position a, Position b)
- {
-#if HASHINDEX
- return a.Endpoint == b.Endpoint ? 0 : a.Endpoint.precedes(b.Endpoint) ? -1 : 1;
-#else
- return a.Index.CompareTo(b.Index);
-#endif
- }
- }
- /// <summary>
- /// During RemoveAll, we need to cache the original endpoint indices of views
- /// </summary>
- struct Position
- {
- public readonly LinkedList<T> View;
- public bool Left;
-#if HASHINDEX
- public readonly Node Endpoint;
-#else
- public readonly int Index;
-#endif
- public Position(LinkedList<T> view, bool left)
- {
- View = view;
- Left = left;
-#if HASHINDEX
- Endpoint = left ? view.startsentinel.next : view.endsentinel.prev;
-#else
- Index = left ? view.Offset : view.Offset + view.size - 1;
-#endif
- }
-#if HASHINDEX
- public Position(Node node, int foo) { this.Endpoint = node; View = null; Left = false; }
-#else
- public Position(int index) { this.Index = index; View = null; Left = false; }
-#endif
- }
-
- //TODO: merge the two implementations using Position values as arguments
- /// <summary>
- /// Handle the update of (other) views during a multi-remove operation.
- /// </summary>
- struct ViewHandler
- {
- ArrayList<Position> leftEnds;
- ArrayList<Position> rightEnds;
- int leftEndIndex, rightEndIndex, leftEndIndex2, rightEndIndex2;
- internal readonly int viewCount;
- internal ViewHandler(LinkedList<T> list)
- {
- leftEndIndex = rightEndIndex = leftEndIndex2 = rightEndIndex2 = viewCount = 0;
- leftEnds = rightEnds = null;
- if (list.views != null)
- foreach (LinkedList<T> v in list.views)
- if (v != list)
- {
- if (leftEnds == null)
- {
- leftEnds = new ArrayList<Position>();
- rightEnds = new ArrayList<Position>();
- }
- leftEnds.Add(new Position(v, true));
- rightEnds.Add(new Position(v, false));
- }
- if (leftEnds == null)
- return;
- viewCount = leftEnds.Count;
- leftEnds.Sort(PositionComparer.Default);
- rightEnds.Sort(PositionComparer.Default);
- }
-#if HASHINDEX
- internal void skipEndpoints(int removed, Node n)
- {
- if (viewCount > 0)
- {
- Position endpoint;
- while (leftEndIndex < viewCount && ((endpoint = leftEnds[leftEndIndex]).Endpoint.prev.precedes(n)))
- {
- LinkedList<T> view = endpoint.View;
- view.offset = view.offset - removed;//TODO: extract offset.Value?
- view.size += removed;
- leftEndIndex++;
- }
- while (rightEndIndex < viewCount && (endpoint = rightEnds[rightEndIndex]).Endpoint.precedes(n))
- {
- LinkedList<T> view = endpoint.View;
- view.size -= removed;
- rightEndIndex++;
- }
- }
- if (viewCount > 0)
- {
- Position endpoint;
- while (leftEndIndex2 < viewCount && (endpoint = leftEnds[leftEndIndex2]).Endpoint.prev.precedes(n))
- leftEndIndex2++;
- while (rightEndIndex2 < viewCount && (endpoint = rightEnds[rightEndIndex2]).Endpoint.next.precedes(n))
- rightEndIndex2++;
- }
- }
- /// <summary>
- /// To be called with n pointing to the right of each node to be removed in a stretch.
- /// And at the endsentinel.
- ///
- /// Update offset of a view whose left endpoint (has not already been handled and) is n or precedes n.
- /// I.e. startsentinel precedes n.
- /// Also update the size as a prelude to handling the right endpoint.
- ///
- /// Update size of a view not already handled and whose right endpoint precedes n.
- /// </summary>
- /// <param name="removed">The number of nodes left of n to be removed</param>
- /// <param name="n"></param>
- internal void updateViewSizesAndCounts(int removed, Node n)
- {
- if (viewCount > 0)
- {
- Position endpoint;
- while (leftEndIndex < viewCount && ((endpoint = leftEnds[leftEndIndex]).Endpoint.prev.precedes(n)))
- {
- LinkedList<T> view = endpoint.View;
- view.offset = view.offset - removed; //TODO: fix use of offset
- view.size += removed;
- leftEndIndex++;
- }
- while (rightEndIndex < viewCount && (endpoint = rightEnds[rightEndIndex]).Endpoint.precedes(n))
- {
- LinkedList<T> view = endpoint.View;
- view.size -= removed;
- rightEndIndex++;
- }
- }
- }
- /// <summary>
- /// To be called with n being the first not-to-be-removed node after a (stretch of) node(s) to be removed.
- ///
- /// It will update the startsentinel of views (that have not been handled before and)
- /// whose startsentinel precedes n, i.e. is to be deleted.
- ///
- /// It will update the endsentinel of views (...) whose endsentinel precedes n, i.e. is to be deleted.
- ///
- /// PROBLEM: DOESNT WORK AS ORIGINALLY ADVERTISED. WE MUST DO THIS BEFORE WE ACTUALLY REMOVE THE NODES. WHEN THE
- /// NODES HAVE BEEN REMOVED, THE precedes METHOD WILL NOT WORK!
- /// </summary>
- /// <param name="n"></param>
- /// <param name="newstart"></param>
- /// <param name="newend"></param>
- internal void updateSentinels(Node n, Node newstart, Node newend)
- {
- if (viewCount > 0)
- {
- Position endpoint;
- while (leftEndIndex2 < viewCount && (endpoint = leftEnds[leftEndIndex2]).Endpoint.prev.precedes(n))
- {
- LinkedList<T> view = endpoint.View;
- view.startsentinel = newstart;
- leftEndIndex2++;
- }
- while (rightEndIndex2 < viewCount && (endpoint = rightEnds[rightEndIndex2]).Endpoint.next.precedes(n))
- {
- LinkedList<T> view = endpoint.View;
- view.endsentinel = newend;
- rightEndIndex2++;
- }
- }
- }
-#else
- /// <summary>
- /// This is to be called with realindex pointing to the first node to be removed after a (stretch of) node that was not removed
- /// </summary>
- /// <param name="removed"></param>
- /// <param name="realindex"></param>
- internal void skipEndpoints(int removed, int realindex)
- {
- if (viewCount > 0)
- {
- Position endpoint;
- while (leftEndIndex < viewCount && (endpoint = leftEnds[leftEndIndex]).Index <= realindex)
- {
- LinkedList<T> view = endpoint.View;
- view.offset = view.offset - removed;
- view.size += removed;
- leftEndIndex++;
- }
- while (rightEndIndex < viewCount && (endpoint = rightEnds[rightEndIndex]).Index < realindex)
- {
- LinkedList<T> view = endpoint.View;
- view.size -= removed;
- rightEndIndex++;
- }
- }
- if (viewCount > 0)
- {
- Position endpoint;
- while (leftEndIndex2 < viewCount && (endpoint = leftEnds[leftEndIndex2]).Index <= realindex)
- leftEndIndex2++;
- while (rightEndIndex2 < viewCount && (endpoint = rightEnds[rightEndIndex2]).Index < realindex - 1)
- rightEndIndex2++;
- }
- }
- internal void updateViewSizesAndCounts(int removed, int realindex)
- {
- if (viewCount > 0)
- {
- Position endpoint;
- while (leftEndIndex < viewCount && (endpoint = leftEnds[leftEndIndex]).Index <= realindex)
- {
- LinkedList<T> view = endpoint.View;
- view.offset = view.Offset - removed;
- view.size += removed;
- leftEndIndex++;
- }
- while (rightEndIndex < viewCount && (endpoint = rightEnds[rightEndIndex]).Index < realindex)
- {
- LinkedList<T> view = endpoint.View;
- view.size -= removed;
- rightEndIndex++;
- }
- }
- }
- internal void updateSentinels(int realindex, Node newstart, Node newend)
- {
- if (viewCount > 0)
- {
- Position endpoint;
- while (leftEndIndex2 < viewCount && (endpoint = leftEnds[leftEndIndex2]).Index <= realindex)
- {
- LinkedList<T> view = endpoint.View;
- view.startsentinel = newstart;
- leftEndIndex2++;
- }
- while (rightEndIndex2 < viewCount && (endpoint = rightEnds[rightEndIndex2]).Index < realindex - 1)
- {
- LinkedList<T> view = endpoint.View;
- view.endsentinel = newend;
- rightEndIndex2++;
- }
- }
- }
-#endif
- }
- #endregion
-
- #region Range nested class
-
- class Range : DirectedCollectionValueBase<T>, IDirectedCollectionValue<T>
- {
- int start, count, rangestamp;
- Node startnode, endnode;
-
- LinkedList<T> list;
-
- bool forwards;
-
-
- internal Range(LinkedList<T> list, int start, int count, bool forwards)
- {
- this.list = list; this.rangestamp = list.underlying != null ? list.underlying.stamp : list.stamp;
- this.start = start; this.count = count; this.forwards = forwards;
- if (count > 0)
- {
- startnode = list.get(start);
- endnode = list.get(start + count - 1);
- }
- }
-
- public override bool IsEmpty { get { list.modifycheck(rangestamp); return count == 0; } }
-
- [Tested]
- public override int Count { [Tested]get { list.modifycheck(rangestamp); return count; } }
-
-
- public override Speed CountSpeed { get { list.modifycheck(rangestamp); return Speed.Constant; } }
-
-
- public override T Choose()
- {
- list.modifycheck(rangestamp);
- if (count > 0) return startnode.item;
- throw new NoSuchItemException();
- }
-
-
- [Tested]
- public override SCG.IEnumerator<T> GetEnumerator()
- {
- int togo = count;
-
- list.modifycheck(rangestamp);
- if (togo == 0)
- yield break;
-
- Node cursor = forwards ? startnode : endnode;
-
- yield return cursor.item;
- while (--togo > 0)
- {
- cursor = forwards ? cursor.next : cursor.prev;
- list.modifycheck(rangestamp);
- yield return cursor.item;
- }
- }
-
-
- [Tested]
- public override IDirectedCollectionValue<T> Backwards()
- {
- list.modifycheck(rangestamp);
-
- Range b = (Range)MemberwiseClone();
-
- b.forwards = !forwards;
- return b;
- }
-
-
- [Tested]
- IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return Backwards(); }
-
-
- [Tested]
- public override EnumerationDirection Direction
- {
- [Tested]
- get
- { return forwards ? EnumerationDirection.Forwards : EnumerationDirection.Backwards; }
- }
- }
-
-
- #endregion
-
- #region IDisposable Members
-
- /// <summary>
- /// Invalidate this list. If a view, just invalidate the view.
- /// If not a view, invalidate the list and all views on it.
- /// </summary>
- public virtual void Dispose()
- {
- Dispose(false);
- }
-
- void Dispose(bool disposingUnderlying)
- {
- if (isValid)
- {
- if (underlying != null)
- {
- isValid = false;
- if (!disposingUnderlying && views != null)
- views.Remove(myWeakReference);
- endsentinel = null;
- startsentinel = null;
- underlying = null;
- views = null;
- myWeakReference = null;
- }
- else
- {
- //isValid = false;
- //endsentinel = null;
- //startsentinel = null;
- if (views != null)
- foreach (LinkedList<T> view in views)
- view.Dispose(true);
- //views = null;
- Clear();
- }
- }
- }
-
- #endregion IDisposable stuff
-
- #region IList<T> Members
-
- /// <summary>
- /// </summary>
- /// <exception cref="NoSuchItemException"> if this list is empty.</exception>
- /// <value>The first item in this list.</value>
- [Tested]
- public virtual T First
- {
- [Tested]
- get
- {
- validitycheck();
- if (size == 0)
- throw new NoSuchItemException();
- return startsentinel.next.item;
- }
- }
-
-
- /// <summary>
- /// </summary>
- /// <exception cref="NoSuchItemException"> if this list is empty.</exception>
- /// <value>The last item in this list.</value>
- [Tested]
- public virtual T Last
- {
- [Tested]
- get
- {
- validitycheck();
- if (size == 0)
- throw new NoSuchItemException();
- return endsentinel.prev.item;
- }
- }
-
- /// <summary>
- /// Since <code>Add(T item)</code> always add at the end of the list,
- /// this describes if list has FIFO or LIFO semantics.
- /// </summary>
- /// <value>True if the <code>Remove()</code> operation removes from the
- /// start of the list, false if it removes from the end. THe default for a new linked list is true.</value>
- [Tested]
- public virtual bool FIFO
- {
- [Tested]
- get { validitycheck(); return fIFO; }
- [Tested]
- set { updatecheck(); fIFO = value; }
- }
-
- /// <summary>
- ///
- /// </summary>
- public virtual bool IsFixedSize
- {
- get { validitycheck(); return false; }
- }
-
- /// <summary>
- /// On this list, this indexer is read/write.
- /// <exception cref="IndexOutOfRangeException"/> if i is negative or
- /// >= the size of the collection.
- /// </summary>
- /// <value>The i'th item of this list.</value>
- /// <param name="index">The index of the item to fetch or store.</param>
- [Tested]
- public virtual T this[int index]
- {
- [Tested]
- get { validitycheck(); return get(index).item; }
- [Tested]
- set
- {
- updatecheck();
- Node n = get(index);
- //
- T item = n.item;
-#if HASHINDEX
-
- if (itemequalityComparer.Equals(value, item))
- {
- n.item = value;
- dict.Update(value, n);
- }
- else if (!dict.FindOrAdd(value, ref n))
- {
- dict.Remove(item);
- n.item = value;
- }
- else
- throw new ArgumentException("Item already in indexed list");
-#else
- n.item = value;
-#endif
- (underlying ?? this).raiseForSetThis(index, value, item);
- }
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public virtual Speed IndexingSpeed { get { return Speed.Linear; } }
-
- /// <summary>
- /// Insert an item at a specific index location in this list.
- /// <exception cref="IndexOutOfRangeException"/> if i is negative or
- /// > the size of the collection.</summary>
- /// <param name="i">The index at which to insert.</param>
- /// <param name="item">The item to insert.</param>
- [Tested]
- public virtual void Insert(int i, T item)
- {
- updatecheck();
- insert(i, i == size ? endsentinel : get(i), item);
- if (ActiveEvents != EventTypeEnum.None)
- (underlying ?? this).raiseForInsert(i + Offset, item);
- }
-
- /// <summary>
- /// Insert an item at the end of a compatible view, used as a pointer.
- /// <para>The <code>pointer</code> must be a view on the same list as
- /// <code>this</code> and the endpoitn of <code>pointer</code> must be
- /// a valid insertion point of <code>this</code></para>
- /// </summary>
- /// <exception cref="IncompatibleViewException">If <code>pointer</code>
- /// is not a view on the same list as <code>this</code></exception>
- /// <exception cref="IndexOutOfRangeException"><b>??????</b> if the endpoint of
- /// <code>pointer</code> is not inside <code>this</code></exception>
- /// <exception cref="DuplicateNotAllowedException"> if the list has
- /// <code>AllowsDuplicates==false</code> and the item is
- /// already in the list.</exception>
- /// <param name="pointer"></param>
- /// <param name="item"></param>
- public void Insert(IList<T> pointer, T item)
- {
- updatecheck();
- if ((pointer == null) || ((pointer.Underlying ?? pointer) != (underlying ?? this)))
- throw new IncompatibleViewException();
-#warning INEFFICIENT
- //TODO: make this efficient (the whole point of the method:
- //Do NOT use Insert, but insert the node at pointer.endsentinel, checking
- //via the ordering that this is a valid insertion point
- Insert(pointer.Offset + pointer.Count - Offset, item);
- }
-
- /// <summary>
- /// Insert into this list all items from an enumerable collection starting
- /// at a particular index.
- /// <exception cref="IndexOutOfRangeException"/> if i is negative or
- /// > the size of the collection.
- /// </summary>
- /// <param name="i">Index to start inserting at</param>
- /// <param name="items">Items to insert</param>
- /// <typeparam name="U"></typeparam>
- [Tested]
- public virtual void InsertAll<U>(int i, SCG.IEnumerable<U> items) where U : T
- {
- insertAll(i, items, true);
- }
-
- void insertAll<U>(int i, SCG.IEnumerable<U> items, bool insertion) where U : T
- {
- updatecheck();
- Node succ, node, pred;
- int count = 0;
- succ = i == size ? endsentinel : get(i);
- pred = node = succ.prev;
-#if HASHINDEX
- TagGroup taggroup = null;
- int taglimit = 0, thetag = 0;
- taggroup = gettaggroup(node, succ, out thetag, out taglimit);
- try
- {
- foreach (T item in items)
- {
- Node tmp = new Node(item, node, null);
- if (!dict.FindOrAdd(item, ref tmp))
- {
- tmp.tag = thetag < taglimit ? ++thetag : thetag;
- tmp.taggroup = taggroup;
- node.next = tmp;
- count++;
- node = tmp;
- }
- else
- throw new DuplicateNotAllowedException("Item already in indexed list");
- }
- }
- finally
- {
- if (count != 0)
- {
- taggroup.count += count;
- if (taggroup != pred.taggroup)
- taggroup.first = pred.next;
- if (taggroup != succ.taggroup)
- taggroup.last = node;
- succ.prev = node;
- node.next = succ;
- if (node.tag == node.prev.tag)
- splittaggroup(taggroup);
- size += count;
- if (underlying != null)
- underlying.size += count;
- fixViewsAfterInsert(succ, pred, count, 0);
- raiseForInsertAll(pred, i, count, insertion);
- }
- }
-#else
- foreach (T item in items)
- {
- Node tmp = new Node(item, node, null);
- node.next = tmp;
- count++;
- node = tmp;
- }
- if (count == 0)
- return;
- succ.prev = node;
- node.next = succ;
- size += count;
- if (underlying != null)
- underlying.size += count;
- if (count > 0)
- {
- fixViewsAfterInsert(succ, pred, count, offset + i);
- raiseForInsertAll(pred, i, count, insertion);
- }
-#endif
- }
-
- private void raiseForInsertAll(Node node, int i, int added, bool insertion)
- {
- if (ActiveEvents != 0)
- {
- int index = Offset + i;
- if ((ActiveEvents & (EventTypeEnum.Added | EventTypeEnum.Inserted)) != 0)
- for (int j = index; j < index + added; j++)
- {
-#warning must we check stamps here?
- node = node.next;
- T item = node.item;
- if (insertion) raiseItemInserted(item, j);
- raiseItemsAdded(item, 1);
- }
- raiseCollectionChanged();
- }
- }
-
- /// <summary>
- /// Insert an item at the front of this list.
- /// </summary>
- /// <param name="item">The item to insert.</param>
- [Tested]
- public virtual void InsertFirst(T item)
- {
- updatecheck();
- insert(0, startsentinel.next, item);
- if (ActiveEvents != EventTypeEnum.None)
- (underlying ?? this).raiseForInsert(0 + Offset, item);
- }
-
- /// <summary>
- /// Insert an item at the back of this list.
- /// </summary>
- /// <param name="item">The item to insert.</param>
- [Tested]
- public virtual void InsertLast(T item)
- {
- updatecheck();
- insert(size, endsentinel, item);
- if (ActiveEvents != EventTypeEnum.None)
- (underlying ?? this).raiseForInsert(size - 1 + Offset, item);
- }
-
- /// <summary>
- /// Create a new list consisting of the results of mapping all items of this
- /// list.
- /// </summary>
- /// <param name="mapper">The delegate defining the map.</param>
- /// <returns>The new list.</returns>
- [Tested]
- public IList<V> Map<V>(Fun<T, V> mapper)
- {
- validitycheck();
-
- LinkedList<V> retval = new LinkedList<V>();
- return map<V>(mapper, retval);
- }
-
- /// <summary>
- /// Create a new list consisting of the results of mapping all items of this
- /// list. The new list will use a specified equalityComparer for the item type.
- /// </summary>
- /// <typeparam name="V">The type of items of the new list</typeparam>
- /// <param name="mapper">The delegate defining the map.</param>
- /// <param name="equalityComparer">The equalityComparer to use for the new list</param>
- /// <returns>The new list.</returns>
- public IList<V> Map<V>(Fun<T, V> mapper, SCG.IEqualityComparer<V> equalityComparer)
- {
- validitycheck();
-
- LinkedList<V> retval = new LinkedList<V>(equalityComparer);
- return map<V>(mapper, retval);
- }
-
- private IList<V> map<V>(Fun<T, V> mapper, LinkedList<V> retval)
- {
- if (size == 0)
- return retval;
- int stamp = this.stamp;
- Node cursor = startsentinel.next;
- LinkedList<V>.Node mcursor = retval.startsentinel;
-
-#if HASHINDEX
- double tagdelta = int.MaxValue / (size + 1.0);
- int count = 1;
- LinkedList<V>.TagGroup taggroup = null;
- taggroup = new LinkedList<V>.TagGroup();
- retval.taggroups = 1;
- taggroup.count = size;
-#endif
- while (cursor != endsentinel)
- {
- V v = mapper(cursor.item);
- modifycheck(stamp);
- mcursor.next = new LinkedList<V>.Node(v, mcursor, null);
- cursor = cursor.next;
- mcursor = mcursor.next;
-#if HASHINDEX
- retval.dict.Add(v, mcursor);
- mcursor.taggroup = taggroup;
- mcursor.tag = (int)(tagdelta * count++);
-#endif
- }
-
-#if HASHINDEX
- taggroup.first = retval.startsentinel.next;
- taggroup.last = mcursor;
-#endif
- retval.endsentinel.prev = mcursor;
- mcursor.next = retval.endsentinel;
- retval.size = size;
- return retval;
- }
-
- /// <summary>
- /// Remove one item from the list: from the front if <code>FIFO</code>
- /// is true, else from the back.
- /// <exception cref="NoSuchItemException"/> if this list is empty.
- /// </summary>
- /// <returns>The removed item.</returns>
- [Tested]
- public virtual T Remove()
- {
- updatecheck();
- if (size == 0)
- throw new NoSuchItemException("List is empty");
- T item = fIFO ? remove(startsentinel.next, 0) : remove(endsentinel.prev, size - 1);
-#if HASHINDEX
- dict.Remove(item);
-#endif
- (underlying ?? this).raiseForRemove(item);
- return item;
- }
-
- /// <summary>
- /// Remove one item from the front of the list.
- /// <exception cref="NoSuchItemException"/> if this list is empty.
- /// </summary>
- /// <returns>The removed item.</returns>
- [Tested]
- public virtual T RemoveFirst()
- {
- updatecheck();
- if (size == 0)
- throw new NoSuchItemException("List is empty");
-
- T item = remove(startsentinel.next, 0);
-#if HASHINDEX
- dict.Remove(item);
-#endif
- if (ActiveEvents != EventTypeEnum.None)
- (underlying ?? this).raiseForRemoveAt(Offset, item);
- return item;
- }
-
- /// <summary>
- /// Remove one item from the back of the list.
- /// <exception cref="NoSuchItemException"/> if this list is empty.
- /// </summary>
- /// <returns>The removed item.</returns>
- [Tested]
- public virtual T RemoveLast()
- {
- updatecheck();
- if (size == 0)
- throw new NoSuchItemException("List is empty");
-
- T item = remove(endsentinel.prev, size - 1);
-#if HASHINDEX
- dict.Remove(item);
-#endif
- if (ActiveEvents != EventTypeEnum.None)
- (underlying ?? this).raiseForRemoveAt(size + Offset, item);
- return item;
- }
-
- /// <summary>
- /// Create a list view on this list.
- /// </summary>
- /// <exception cref="ArgumentOutOfRangeException"> if the start or count is negative</exception>
- /// <exception cref="ArgumentException"> if the range does not fit within list.</exception>
- /// <param name="start">The index in this list of the start of the view.</param>
- /// <param name="count">The size of the view.</param>
- /// <returns>The new list view.</returns>
- [Tested]
- public virtual IList<T> View(int start, int count)
- {
- checkRange(start, count);
- validitycheck();
- if (views == null)
- views = new WeakViewList<LinkedList<T>>();
- LinkedList<T> retval = (LinkedList<T>)MemberwiseClone();
- retval.underlying = underlying != null ? underlying : this;
- retval.offset = offset + start;
- retval.size = count;
- getPair(start - 1, start + count, out retval.startsentinel, out retval.endsentinel,
- new int[] { -1, size }, new Node[] { startsentinel, endsentinel });
- //retval.startsentinel = start == 0 ? startsentinel : get(start - 1);
- //retval.endsentinel = start + count == size ? endsentinel : get(start + count);
-
- //TODO: for the purpose of Dispose, we need to retain a ref to the node
- retval.myWeakReference = views.Add(retval);
- return retval;
- }
-
- /// <summary>
- /// Create a list view on this list containing the (first) occurrence of a particular item.
- /// </summary>
- /// <exception cref="ArgumentException"> if the item is not in this list.</exception>
- /// <param name="item">The item to find.</param>
- /// <returns>The new list view.</returns>
- public virtual IList<T> ViewOf(T item)
- {
-#if HASHINDEX
- Node n;
- validitycheck();
- if (!contains(item, out n))
- return null;
- LinkedList<T> retval = (LinkedList<T>)MemberwiseClone();
- retval.underlying = underlying != null ? underlying : this;
- retval.offset = null;
- retval.startsentinel = n.prev;
- retval.endsentinel = n.next;
- retval.size = 1;
- return retval;
-#else
- int index = 0;
- Node n = startsentinel.next;
- if (!find(item, ref n, ref index))
- return null;
- //TODO: optimize with getpair!
- return View(index, 1);
-#endif
- }
-
- /// <summary>
- /// Create a list view on this list containing the last occurrence of a particular item.
- /// <exception cref="ArgumentException"/> if the item is not in this list.
- /// </summary>
- /// <param name="item">The item to find.</param>
- /// <returns>The new list view.</returns>
- public virtual IList<T> LastViewOf(T item)
- {
-#if HASHINDEX
- return ViewOf(item);
-#else
- int index = size - 1;
- Node n = endsentinel.prev;
- if (!dnif(item, ref n, ref index))
- return null;
- return View(index, 1);
-#endif
- }
-
- /// <summary>
- /// Null if this list is not a view.
- /// </summary>
- /// <value>Underlying list for view.</value>
- [Tested]
- public virtual IList<T> Underlying { [Tested]get { validitycheck(); return underlying; } }
-
- /// <summary>
- ///
- /// </summary>
- /// <value></value>
- public virtual bool IsValid { get { return isValid; } }
-
- /// <summary>
- /// </summary>
- /// <value>Offset for this list view or 0 for a underlying list.</value>
- [Tested]
- public virtual int Offset
- {
- [Tested]
- get
- {
- validitycheck();
-#if HASHINDEX
- if (offset == null && underlying != null)
- {
- //TODO: search from both ends simultaneously!
- Node n = underlying.startsentinel;
- int i = 0;
- while (n != startsentinel) { n = n.next; i++; }
- offset = i;
- }
-#endif
- return (int)offset;
- }
- }
-
- /// <summary>
- /// Slide this list view along the underlying list.
- /// </summary>
- /// <exception cref="NotAViewException"> if this list is not a view.</exception>
- /// <exception cref="ArgumentOutOfRangeException"> if the operation
- /// would bring either end of the view outside the underlying list.</exception>
- /// <param name="offset">The signed amount to slide: positive to slide
- /// towards the end.</param>
- [Tested]
- public IList<T> Slide(int offset)
- {
- if (!TrySlide(offset, size))
- throw new ArgumentOutOfRangeException();
- return this;
- }
-
- //TODO: more test cases
- /// <summary>
- /// Slide this list view along the underlying list, perhaps changing its size.
- /// </summary>
- /// <exception cref="NotAViewException"> if this list is not a view.</exception>
- /// <exception cref="ArgumentOutOfRangeException"> if the operation
- /// would bring either end of the view outside the underlying list.</exception>
- /// <param name="offset">The signed amount to slide: positive to slide
- /// towards the end.</param>
- /// <param name="size">The new size of the view.</param>
- public IList<T> Slide(int offset, int size)
- {
- if (!TrySlide(offset, size))
- throw new ArgumentOutOfRangeException();
- return this;
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="offset"></param>
- /// <returns></returns>
- public virtual bool TrySlide(int offset) { return TrySlide(offset, size); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="offset"></param>
- /// <param name="size"></param>
- /// <returns></returns>
- public virtual bool TrySlide(int offset, int size)
- {
- updatecheck();
- if (underlying == null)
- throw new NotAViewException("List not a view");
-
-#pragma warning disable 472
- if (this.offset == null) //Note: only possible with HASHINDEX
-#pragma warning restore 472
- {
-#pragma warning disable 162
- try
- {
- getPair(offset - 1, offset + size, out startsentinel, out endsentinel,
- new int[] { -1, this.size }, new Node[] { startsentinel, endsentinel });
- //TODO: maybe-update offset field
- }
- catch (NullReferenceException)
- {
- return false;
- }
-#pragma warning restore 162
- }
- else
- {
- if (offset + this.offset < 0 || offset + this.offset + size > underlying.size)
- return false;
- int oldoffset = (int)(this.offset);
- getPair(offset - 1, offset + size, out startsentinel, out endsentinel,
- new int[] { -oldoffset - 1, -1, this.size, underlying.size - oldoffset },
- new Node[] { underlying.startsentinel, startsentinel, endsentinel, underlying.endsentinel });
- }
- this.size = size;
- this.offset += offset;
- return true;
- }
-
-
- //TODO: improve the complexity of the implementation
- /// <summary>
- ///
- /// <para>Returns null if <code>otherView</code> is strictly to the left of this view</para>
- /// </summary>
- /// <param name="otherView"></param>
- /// <exception cref="IncompatibleViewException">If otherView does not have the same underlying list as this</exception>
- /// <returns></returns>
- public virtual IList<T> Span(IList<T> otherView)
- {
- if ((otherView == null) || ((otherView.Underlying ?? otherView) != (underlying ?? this)))
- throw new IncompatibleViewException();
- if (otherView.Offset + otherView.Count - Offset < 0)
- return null;
- return (underlying ?? this).View(Offset, otherView.Offset + otherView.Count - Offset);
- }
-
-
- //Question: should we swap items or move nodes around?
- //The first seems much more efficient unless the items are value types
- //with a large memory footprint.
- //(Swapping will do count*3/2 T assignments, linking around will do
- // 4*count ref assignments; note that ref assignments are more expensive
- //than copying non-ref bits)
- /// <summary>
- /// Reverse the list so the items are in the opposite sequence order.
- /// </summary>
- [Tested]
- public virtual void Reverse()
- {
- updatecheck();
- if (size == 0)
- return;
-
- Position[] positions = null;
- int poslow = 0, poshigh = 0;
- if (views != null)
- {
- CircularQueue<Position> _positions = null;
- foreach (LinkedList<T> view in views)
- {
- if (view != this)
- {
- switch (viewPosition(view))
- {
- case MutualViewPosition.ContainedIn:
- (_positions ?? (_positions = new CircularQueue<Position>())).Enqueue(new Position(view, true));
- _positions.Enqueue(new Position(view, false));
- break;
- case MutualViewPosition.Overlapping:
- view.Dispose();
- break;
- case MutualViewPosition.Contains:
- case MutualViewPosition.NonOverlapping:
- break;
- }
- }
- }
- if (_positions != null)
- {
- positions = _positions.ToArray();
- Sorting.IntroSort<Position>(positions, 0, positions.Length, PositionComparer.Default);
- poshigh = positions.Length - 1;
- }
- }
-
- Node a = get(0), b = get(size - 1);
- for (int i = 0; i < size / 2; i++)
- {
- T swap;
- swap = a.item; a.item = b.item; b.item = swap;
-#if HASHINDEX
- dict[a.item] = a; dict[b.item] = b;
-#endif
- if (positions != null)
- mirrorViewSentinelsForReverse(positions, ref poslow, ref poshigh, a, b, i);
- a = a.next; b = b.prev;
- }
- if (positions != null && size % 2 != 0)
- mirrorViewSentinelsForReverse(positions, ref poslow, ref poshigh, a, b, size / 2);
- (underlying ?? this).raiseCollectionChanged();
- }
-
- private void mirrorViewSentinelsForReverse(Position[] positions, ref int poslow, ref int poshigh, Node a, Node b, int i)
- {
-#if HASHINDEX
- int? aindex = offset + i, bindex = offset + size - 1 - i;
-#else
- int aindex = offset + i, bindex = offset + size - 1 - i;
-#endif
- Position pos;
-#if HASHINDEX
- while (poslow <= poshigh && (pos = positions[poslow]).Endpoint == a)
-#else
- while (poslow <= poshigh && (pos = positions[poslow]).Index == aindex)
-#endif
- {
- //TODO: Note: in the case og hashed linked list, if this.offset == null, but pos.View.offset!=null
- //we may at this point compute this.offset and non-null values of aindex and bindex
- if (pos.Left)
- pos.View.endsentinel = b.next;
- else
- {
- pos.View.startsentinel = b.prev;
- pos.View.offset = bindex;
- }
- poslow++;
- }
-#if HASHINDEX
- while (poslow < poshigh && (pos = positions[poshigh]).Endpoint == b)
-#else
- while (poslow < poshigh && (pos = positions[poshigh]).Index == bindex)
-#endif
- {
- if (pos.Left)
- pos.View.endsentinel = a.next;
- else
- {
- pos.View.startsentinel = a.prev;
- pos.View.offset = aindex;
- }
- poshigh--;
- }
- }
-
- /// <summary>
- /// Check if this list is sorted according to the default sorting order
- /// for the item type T, as defined by the <see cref="T:C5.Comparer`1"/> class
- /// </summary>
- /// <exception cref="NotComparableException">if T is not comparable</exception>
- /// <returns>True if the list is sorted, else false.</returns>
- public bool IsSorted() { return IsSorted(Comparer<T>.Default); }
-
- /// <summary>
- /// Check if this list is sorted according to a specific sorting order.
- /// </summary>
- /// <param name="c">The comparer defining the sorting order.</param>
- /// <returns>True if the list is sorted, else false.</returns>
- [Tested]
- public virtual bool IsSorted(SCG.IComparer<T> c)
- {
- validitycheck();
- if (size <= 1)
- return true;
-
- Node node = startsentinel.next;
- T prevItem = node.item;
-
- node = node.next;
- while (node != endsentinel)
- {
- if (c.Compare(prevItem, node.item) > 0)
- return false;
- else
- {
- prevItem = node.item;
- node = node.next;
- }
- }
-
- return true;
- }
-
- /// <summary>
- /// Sort the items of the list according to the default sorting order
- /// for the item type T, as defined by the Comparer[T] class.
- /// (<see cref="T:C5.Comparer`1"/>).
- /// The sorting is stable.
- /// </summary>
- /// <exception cref="InvalidOperationException">if T is not comparable</exception>
- public virtual void Sort() { Sort(Comparer<T>.Default); }
-
- // Sort the linked list using mergesort
- /// <summary>
- /// Sort the items of the list according to a specific sorting order.
- /// The sorting is stable.
- /// </summary>
- /// <param name="c">The comparer defining the sorting order.</param>
- [Tested]
- public virtual void Sort(SCG.IComparer<T> c)
- {
- updatecheck();
- if (size == 0)
- return;
- disposeOverlappingViews(false);
-#if HASHINDEX
- if (underlying != null)
- {
- Node cursor = startsentinel.next;
- while (cursor != endsentinel)
- {
- cursor.taggroup.count--;
- cursor = cursor.next;
- }
- }
-#endif
- // Build a linked list of non-empty runs.
- // The prev field in first node of a run points to next run's first node
- Node runTail = startsentinel.next;
- Node prevNode = startsentinel.next;
-
- endsentinel.prev.next = null;
- while (prevNode != null)
- {
- Node node = prevNode.next;
-
- while (node != null && c.Compare(prevNode.item, node.item) <= 0)
- {
- prevNode = node;
- node = prevNode.next;
- }
-
- // Completed a run; prevNode is the last node of that run
- prevNode.next = null; // Finish the run
- runTail.prev = node; // Link it into the chain of runs
- runTail = node;
- if (c.Compare(endsentinel.prev.item, prevNode.item) <= 0)
- endsentinel.prev = prevNode; // Update last pointer to point to largest
-
- prevNode = node; // Start a new run
- }
-
- // Repeatedly merge runs two and two, until only one run remains
- while (startsentinel.next.prev != null)
- {
- Node run = startsentinel.next;
- Node newRunTail = null;
-
- while (run != null && run.prev != null)
- { // At least two runs, merge
- Node nextRun = run.prev.prev;
- Node newrun = mergeRuns(run, run.prev, c);
-
- if (newRunTail != null)
- newRunTail.prev = newrun;
- else
- startsentinel.next = newrun;
-
- newRunTail = newrun;
- run = nextRun;
- }
-
- if (run != null) // Add the last run, if any
- newRunTail.prev = run;
- }
-
- endsentinel.prev.next = endsentinel;
- startsentinel.next.prev = startsentinel;
-
- //assert invariant();
- //assert isSorted();
-#if HASHINDEX
- {
- Node cursor = startsentinel.next, end = endsentinel;
- int tag, taglimit;
- TagGroup t = gettaggroup(startsentinel, endsentinel, out tag, out taglimit);
- int tagdelta = taglimit / (size + 1) - tag / (size + 1);
- tagdelta = tagdelta == 0 ? 1 : tagdelta;
- if (underlying == null)
- taggroups = 1;
- while (cursor != end)
- {
- tag = tag + tagdelta > taglimit ? taglimit : tag + tagdelta;
- cursor.tag = tag;
- t.count++;
- cursor.taggroup = t;
- cursor = cursor.next;
- }
- if (t != startsentinel.taggroup)
- t.first = startsentinel.next;
- if (t != endsentinel.taggroup)
- t.last = endsentinel.prev;
- if (tag == taglimit)
- splittaggroup(t);
- }
-#endif
- (underlying ?? this).raiseCollectionChanged();
- }
-
- private static Node mergeRuns(Node run1, Node run2, SCG.IComparer<T> c)
- {
- //assert run1 != null && run2 != null;
- Node prev;
- bool prev1; // is prev from run1?
-
- if (c.Compare(run1.item, run2.item) <= 0)
- {
- prev = run1;
- prev1 = true;
- run1 = run1.next;
- }
- else
- {
- prev = run2;
- prev1 = false;
- run2 = run2.next;
- }
-
- Node start = prev;
-
- //assert start != null;
- start.prev = null;
- while (run1 != null && run2 != null)
- {
- if (prev1)
- {
- //assert prev.next == run1;
- //Comparable run2item = (Comparable)run2.item;
- while (run1 != null && c.Compare(run2.item, run1.item) >= 0)
- {
- prev = run1;
- run1 = prev.next;
- }
-
- if (run1 != null)
- { // prev.item <= run2.item < run1.item; insert run2
- prev.next = run2;
- run2.prev = prev;
- prev = run2;
- run2 = prev.next;
- prev1 = false;
- }
- }
- else
- {
- //assert prev.next == run2;
- //Comparable run1item = (Comparable)run1.item;
- while (run2 != null && c.Compare(run1.item, run2.item) > 0)
- {
- prev = run2;
- run2 = prev.next;
- }
-
- if (run2 != null)
- { // prev.item < run1.item <= run2.item; insert run1
- prev.next = run1;
- run1.prev = prev;
- prev = run1;
- run1 = prev.next;
- prev1 = true;
- }
- }
- }
-
- //assert !(run1 != null && prev1) && !(run2 != null && !prev1);
- if (run1 != null)
- { // last run2 < all of run1; attach run1 at end
- prev.next = run1;
- run1.prev = prev;
- }
- else if (run2 != null)
- { // last run1
- prev.next = run2;
- run2.prev = prev;
- }
-
- return start;
- }
-
- /// <summary>
- /// Randomly shuffle the items of this list.
- /// <para>Will invalidate overlapping views???</para>
- /// </summary>
- public virtual void Shuffle() { Shuffle(new C5Random()); }
-
-
- /// <summary>
- /// Shuffle the items of this list according to a specific random source.
- /// <para>Will invalidate overlapping views???</para>
- /// </summary>
- /// <param name="rnd">The random source.</param>
- public virtual void Shuffle(Random rnd)
- {
- updatecheck();
- if (size == 0)
- return;
- disposeOverlappingViews(false);
- ArrayList<T> a = new ArrayList<T>();
- a.AddAll(this);
- a.Shuffle(rnd);
- Node cursor = startsentinel.next;
- int j = 0;
- while (cursor != endsentinel)
- {
- cursor.item = a[j++];
-#if HASHINDEX
- dict[cursor.item] = cursor;
-#endif
- cursor = cursor.next;
- }
- (underlying ?? this).raiseCollectionChanged();
- }
-
- #endregion
-
- #region IIndexed<T> Members
-
- /// <summary>
- /// <exception cref="IndexOutOfRangeException"/>.
- /// </summary>
- /// <value>The directed collection of items in a specific index interval.</value>
- /// <param name="start">The low index of the interval (inclusive).</param>
- /// <param name="count">The size of the range.</param>
- [Tested]
- public IDirectedCollectionValue<T> this[int start, int count]
- {
- [Tested]
- get
- {
- validitycheck();
- checkRange(start, count);
- return new Range(this, start, count, true);
- }
- }
-
- /// <summary>
- /// Searches for an item in the list going forwrds from the start.
- /// </summary>
- /// <param name="item">Item to search for.</param>
- /// <returns>Index of item from start.</returns>
- [Tested]
- public virtual int IndexOf(T item)
- {
- validitycheck();
- Node node;
-#if HASHINDEX
- if (!dict.Find(item, out node) || !insideview(node))
- return ~size;
-#endif
- node = startsentinel.next;
- int index = 0;
- if (find(item, ref node, ref index))
- return index;
- else
- return ~size;
- }
-
- /// <summary>
- /// Searches for an item in the list going backwords from the end.
- /// </summary>
- /// <param name="item">Item to search for.</param>
- /// <returns>Index of of item from the end.</returns>
- [Tested]
- public virtual int LastIndexOf(T item)
- {
-#if HASHINDEX
- return IndexOf(item);
-#else
- validitycheck();
-
- Node node = endsentinel.prev;
- int index = size - 1;
-
- if (dnif(item, ref node, ref index))
- return index;
- else
- return ~size;
-#endif
- }
-
- /// <summary>
- /// Remove the item at a specific position of the list.
- /// <exception cref="IndexOutOfRangeException"/> if i is negative or
- /// >= the size of the collection.
- /// </summary>
- /// <param name="i">The index of the item to remove.</param>
- /// <returns>The removed item.</returns>
- [Tested]
- public virtual T RemoveAt(int i)
- {
- updatecheck();
- T retval = remove(get(i), i);
-#if HASHINDEX
- dict.Remove(retval);
-#endif
- if (ActiveEvents != EventTypeEnum.None)
- (underlying ?? this).raiseForRemoveAt(Offset + i, retval);
- return retval;
- }
-
- /// <summary>
- /// Remove all items in an index interval.
- /// <exception cref="IndexOutOfRangeException"/>???.
- /// </summary>
- /// <param name="start">The index of the first item to remove.</param>
- /// <param name="count">The number of items to remove.</param>
- [Tested]
- public virtual void RemoveInterval(int start, int count)
- {
-#if HASHINDEX
- updatecheck();
- checkRange(start, count);
- if (count == 0)
- return;
-
- View(start, count).Clear();
-#else
- //Note: this is really almost equaivalent to Clear on a view
- updatecheck();
- checkRange(start, count);
- if (count == 0)
- return;
-
- //for small count: optimize
- //use an optimal get(int i, int j, ref Node ni, ref Node nj)?
- Node a = get(start), b = get(start + count - 1);
- fixViewsBeforeRemove(start, count, a, b);
- a.prev.next = b.next;
- b.next.prev = a.prev;
- if (underlying != null)
- underlying.size -= count;
-
- size -= count;
- if (ActiveEvents != EventTypeEnum.None)
- (underlying ?? this).raiseForRemoveInterval(start + Offset, count);
-#endif
- }
-
- void raiseForRemoveInterval(int start, int count)
- {
- if (ActiveEvents != 0)
- {
- raiseCollectionCleared(size == 0, count, start);
- raiseCollectionChanged();
- }
- }
- #endregion
-
- #region ISequenced<T> Members
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- [Tested]
- public override int GetSequencedHashCode() { validitycheck(); return base.GetSequencedHashCode(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="that"></param>
- /// <returns></returns>
- [Tested]
- public override bool SequencedEquals(ISequenced<T> that) { validitycheck(); return base.SequencedEquals(that); }
-
- #endregion
-
- #region IDirectedCollection<T> Members
-
- /// <summary>
- /// Create a collection containing the same items as this collection, but
- /// whose enumerator will enumerate the items backwards. The new collection
- /// will become invalid if the original is modified. Method typicaly used as in
- /// <code>foreach (T x in coll.Backwards()) {...}</code>
- /// </summary>
- /// <returns>The backwards collection.</returns>
- [Tested]
- public override IDirectedCollectionValue<T> Backwards()
- { return this[0, size].Backwards(); }
-
- #endregion
-
- #region IDirectedEnumerable<T> Members
-
- [Tested]
- IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return Backwards(); }
-
- #endregion
-
- #region IEditableCollection<T> Members
-
- /// <summary>
- /// The value is symbolic indicating the type of asymptotic complexity
- /// in terms of the size of this collection (worst-case or amortized as
- /// relevant).
- /// </summary>
- /// <value>Speed.Linear</value>
- [Tested]
- public virtual Speed ContainsSpeed
- {
- [Tested]
- get
- {
-#if HASHINDEX
- return Speed.Constant;
-#else
- return Speed.Linear;
-#endif
- }
- }
-
- /// <summary>
- /// Performs a check for view validity before calling base.GetUnsequencedHashCode()
- /// </summary>
- /// <returns></returns>
- [Tested]
- public override int GetUnsequencedHashCode()
- { validitycheck(); return base.GetUnsequencedHashCode(); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="that"></param>
- /// <returns></returns>
- [Tested]
- public override bool UnsequencedEquals(ICollection<T> that)
- { validitycheck(); return base.UnsequencedEquals(that); }
-
- /// <summary>
- /// Check if this collection contains (an item equivalent to according to the
- /// itemequalityComparer) a particular value.
- /// </summary>
- /// <param name="item">The value to check for.</param>
- /// <returns>True if the items is in this collection.</returns>
- [Tested]
- public virtual bool Contains(T item)
- {
- validitycheck();
- Node node;
- return contains(item, out node);
- }
-
- /// <summary>
- /// Check if this collection contains an item equivalent according to the
- /// itemequalityComparer to a particular value. If so, return in the ref argument (a
- /// binary copy of) the actual value found.
- /// </summary>
- /// <param name="item">The value to look for.</param>
- /// <returns>True if the items is in this collection.</returns>
- [Tested]
- public virtual bool Find(ref T item)
- {
- validitycheck();
- Node node;
- if (contains(item, out node)) { item = node.item; return true; }
- return false;
- }
-
- /// <summary>
- /// Check if this collection contains an item equivalent according to the
- /// itemequalityComparer to a particular value. If so, update the item in the collection
- /// to with a binary copy of the supplied value. Will update a single item.
- /// </summary>
- /// <param name="item">Value to update.</param>
- /// <returns>True if the item was found and hence updated.</returns>
- [Tested]
- public virtual bool Update(T item) { T olditem; return Update(item, out olditem); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <param name="olditem"></param>
- /// <returns></returns>
- public virtual bool Update(T item, out T olditem)
- {
- updatecheck();
- Node node;
-
- if (contains(item, out node))
- {
- olditem = node.item;
- node.item = item;
-#if HASHINDEX
- //Avoid clinging onto a reference to olditem via dict!
- dict.Update(item, node);
-#endif
- (underlying ?? this).raiseForUpdate(item, olditem);
- return true;
- }
-
- olditem = default(T);
- return false;
- }
-
- /// <summary>
- /// Check if this collection contains an item equivalent according to the
- /// itemequalityComparer to a particular value. If so, return in the ref argument (a
- /// binary copy of) the actual value found. Else, add the item to the collection.
- /// </summary>
- /// <param name="item">The value to look for.</param>
- /// <returns>True if the item was found (hence not added).</returns>
- [Tested]
- public virtual bool FindOrAdd(ref T item)
- {
- updatecheck();
-#if HASHINDEX
- //This is an extended myinsert:
- Node node = new Node(item);
- if (!dict.FindOrAdd(item, ref node))
- {
- insertNode(true, endsentinel, node);
- (underlying ?? this).raiseForAdd(item);
- return false;
- }
- if (!insideview(node))
- throw new ArgumentException("Item alredy in indexed list but outside view");
- item = node.item;
- return true;
-#else
- if (Find(ref item))
- return true;
-
- Add(item);
- return false;
-#endif
- }
-
- /// <summary>
- /// Check if this collection contains an item equivalent according to the
- /// itemequalityComparer to a particular value. If so, update the item in the collection
- /// to with a binary copy of the supplied value; else add the value to the collection.
- /// </summary>
- /// <param name="item">Value to add or update.</param>
- /// <returns>True if the item was found and updated (hence not added).</returns>
- [Tested]
- public virtual bool UpdateOrAdd(T item) { T olditem; return UpdateOrAdd(item, out olditem); }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="item"></param>
- /// <param name="olditem"></param>
- /// <returns></returns>
- public virtual bool UpdateOrAdd(T item, out T olditem)
- {
- updatecheck();
-#if HASHINDEX
- Node node = new Node(item);
- //NOTE: it is hard to do this without double access to the dictionary
- //in the update case
- if (dict.FindOrAdd(item, ref node))
- {
- if (!insideview(node))
- throw new ArgumentException("Item in indexed list but outside view");
- olditem = node.item;
- //Avoid clinging onto a reference to olditem via dict!
- dict.Update(item, node);
- node.item = item;
- (underlying ?? this).raiseForUpdate(item, olditem);
- return true;
- }
- insertNode(true, endsentinel, node);
- (underlying ?? this).raiseForAdd(item);
-#else
- if (Update(item, out olditem))
- return true;
- Add(item);
-#endif
- olditem = default(T);
- return false;
- }
-
- /// <summary>
- /// Remove a particular item from this collection. Since the collection has bag
- /// semantics only one copy equivalent to the supplied item is removed.
- /// </summary>
- /// <param name="item">The value to remove.</param>
- /// <returns>True if the item was found (and removed).</returns>
- [Tested]
- public virtual bool Remove(T item)
- {
- updatecheck();
- int i = 0;
- Node node;
-#if HASHINDEX
- if (!dictremove(item, out node))
-#else
- node = fIFO ? startsentinel.next : endsentinel.prev;
- if (!(fIFO ? find(item, ref node, ref i) : dnif(item, ref node, ref i)))
-#endif
- return false;
- T removeditem = remove(node, i);
- (underlying ?? this).raiseForRemove(removeditem);
- return true;
- }
-
- /// <summary>
- /// Remove a particular item from this collection if found (only one copy).
- /// If an item was removed, report a binary copy of the actual item removed in
- /// the argument.
- /// </summary>
- /// <param name="item">The value to remove on input.</param>
- /// <param name="removeditem">The value removed.</param>
- /// <returns>True if the item was found (and removed).</returns>
- [Tested]
- public virtual bool Remove(T item, out T removeditem)
- {
- updatecheck();
- int i = 0;
- Node node;
-#if HASHINDEX
- if (!dictremove(item, out node))
-#else
- node = fIFO ? startsentinel.next : endsentinel.prev;
- if (!(fIFO ? find(item, ref node, ref i) : dnif(item, ref node, ref i)))
-#endif
- {
- removeditem = default(T);
- return false;
- }
- removeditem = node.item;
- remove(node, i);
- (underlying ?? this).raiseForRemove(removeditem);
- return true;
- }
-
- /// <summary>
- /// Remove all items in another collection from this one, taking multiplicities into account.
- /// <para>Always removes from the front of the list.
- /// </para>
- /// <para>The asymptotic running time complexity of this method is <code>O(n+m+v*log(v))</code>,
- /// where <code>n</code> is the size of this list, <code>m</code> is the size of the
- /// <code>items</code> collection and <code>v</code> is the number of views.
- /// The method will temporarily allocate memory of size <code>O(m+v)</code>.
- /// </para>
- /// </summary>
- /// <typeparam name="U"></typeparam>
- /// <param name="items">The items to remove.</param>
- [Tested]
- public virtual void RemoveAll<U>(SCG.IEnumerable<U> items) where U : T
- {
- updatecheck();
- if (size == 0)
- return;
- RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this);
- bool mustFire = raiseHandler.MustFire;
-#if HASHINDEX
- Node node;
- foreach (T item in items)
- if (dictremove(item, out node))
- {
- if (mustFire)
- raiseHandler.Remove(node.item);
- remove(node, 118);
- }
-#else
- HashBag<T> toremove = new HashBag<T>(itemequalityComparer);
- toremove.AddAll(items);
- ViewHandler viewHandler = new ViewHandler(this);
- int index = 0, removed = 0, myoffset = Offset;
- Node node = startsentinel.next;
- while (node != endsentinel)
- {
- //pass by a stretch of nodes
- while (node != endsentinel && !toremove.Contains(node.item))
- {
- node = node.next;
- index++;
- }
- viewHandler.skipEndpoints(removed, myoffset + index);
- //Remove a stretch of nodes
- Node localend = node.prev; //Latest node not to be removed
- while (node != endsentinel && toremove.Remove(node.item))
- {
- if (mustFire)
- raiseHandler.Remove(node.item);
- removed++;
- node = node.next;
- index++;
- viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
- }
- viewHandler.updateSentinels(myoffset + index, localend, node);
- localend.next = node;
- node.prev = localend;
- }
- index = underlying != null ? underlying.size + 1 - myoffset : size + 1 - myoffset;
- viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
- size -= removed;
- if (underlying != null)
- underlying.size -= removed;
-#endif
- raiseHandler.Raise();
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="predicate"></param>
- void RemoveAll(Fun<T, bool> predicate)
- {
- updatecheck();
- if (size == 0)
- return;
- RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this);
- bool mustFire = raiseHandler.MustFire;
-#if HASHINDEX
- {
- Node n = startsentinel.next;
-
- while (n != endsentinel)
- {
- bool removeIt = predicate(n.item);
- updatecheck();
- if (removeIt)
- {
- dict.Remove(n.item);
- remove(n, 119);
- if (mustFire)
- raiseHandler.Remove(n.item);
- }
-
- n = n.next;
- }
- }
-#else
- ViewHandler viewHandler = new ViewHandler(this);
- int index = 0, removed = 0, myoffset = Offset;
- Node node = startsentinel.next;
- while (node != endsentinel)
- {
- //pass by a stretch of nodes
- while (node != endsentinel && !predicate(node.item))
- {
- updatecheck();
- node = node.next;
- index++;
- }
- updatecheck();
- viewHandler.skipEndpoints(removed, myoffset + index);
- //Remove a stretch of nodes
- Node localend = node.prev; //Latest node not to be removed
- while (node != endsentinel && predicate(node.item))
- {
- updatecheck();
- if (mustFire)
- raiseHandler.Remove(node.item);
- removed++;
- node = node.next;
- index++;
- viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
- }
- updatecheck();
- viewHandler.updateSentinels(myoffset + index, localend, node);
- localend.next = node;
- node.prev = localend;
- }
- index = underlying != null ? underlying.size + 1 - myoffset : size + 1 - myoffset;
- viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
- size -= removed;
- if (underlying != null)
- underlying.size -= removed;
-#endif
- raiseHandler.Raise();
- }
-
- /// <summary>
- /// Remove all items from this collection.
- /// </summary>
- [Tested]
- public virtual void Clear()
- {
- updatecheck();
- if (size == 0)
- return;
- int oldsize = size;
-#if HASHINDEX
- if (underlying == null)
- dict.Clear();
- else
- foreach (T item in this)
- dict.Remove(item);
-#endif
- clear();
- (underlying ?? this).raiseForRemoveInterval(Offset, oldsize);
- }
-
- void clear()
- {
- if (size == 0)
- return;
-#if HASHINDEX
- //TODO: mix with tag maintenance to only run through list once?
- ViewHandler viewHandler = new ViewHandler(this);
- if (viewHandler.viewCount > 0)
- {
- int removed = 0;
- Node n = startsentinel.next;
- viewHandler.skipEndpoints(0, n);
- while (n != endsentinel)
- {
- removed++;
- n = n.next;
- viewHandler.updateViewSizesAndCounts(removed, n);
- }
- viewHandler.updateSentinels(endsentinel, startsentinel, endsentinel);
- if (underlying != null)
- viewHandler.updateViewSizesAndCounts(removed, underlying.endsentinel);
- }
-#else
- fixViewsBeforeRemove(Offset, size, startsentinel.next, endsentinel.prev);
-#endif
-#if HASHINDEX
- if (underlying != null)
- {
- Node n = startsentinel.next;
-
- while (n != endsentinel)
- {
- n.next.prev = startsentinel;
- startsentinel.next = n.next;
- removefromtaggroup(n);
- n = n.next;
- }
- }
- else
- taggroups = 0;
-#endif
- endsentinel.prev = startsentinel;
- startsentinel.next = endsentinel;
- if (underlying != null)
- underlying.size -= size;
- size = 0;
- }
-
- /// <summary>
- /// Remove all items not in some other collection from this one, taking multiplicities into account.
- /// <para>The asymptotic running time complexity of this method is <code>O(n+m+v*log(v))</code>,
- /// where <code>n</code> is the size of this collection, <code>m</code> is the size of the
- /// <code>items</code> collection and <code>v</code> is the number of views.
- /// The method will temporarily allocate memory of size <code>O(m+v)</code>. The stated complexitiy
- /// holds under the assumption that the itemequalityComparer of this list is well-behaved.
- /// </para>
- /// </summary>
- /// <typeparam name="U"></typeparam>
- /// <param name="items">The items to retain.</param>
- [Tested]
- public virtual void RetainAll<U>(SCG.IEnumerable<U> items) where U : T
- {
- updatecheck();
- if (size == 0)
- return;
- RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this);
- bool mustFire = raiseHandler.MustFire;
-#if HASHINDEX
- /*if (underlying == null)
- {
- HashDictionary<T, Node> newdict = new HashDictionary<T, Node>(itemequalityComparer);
- foreach (T item in items)
- {
- Node node;
-
- if (dict.Remove(item, out node))
- newdict.Add(item, node);
- }
- foreach (KeyValuePair<T, Node> pair in dict)
- {
- Node n = pair.Value;
- fixViewsBeforeSingleRemove(n, 117);
- Node p = n.prev, s = n.next; s.prev = p; p.next = s;
- removefromtaggroup(n);
- }
- dict = newdict;
- size = dict.Count;
- //For a small number of items to retain it might be faster to
- //iterate through the list and splice out the chunks not needed
- }
- else*/
- {
- HashSet<T> toremove = new HashSet<T>(itemequalityComparer);
-
- foreach (T item in this)
- toremove.Add(item);
-
- foreach (T item in items)
- toremove.Remove(item);
-
- Node n = startsentinel.next;
-
- while (n != endsentinel && toremove.Count > 0)
- {
- if (toremove.Contains(n.item))
- {
- dict.Remove(n.item);
- remove(n, 119);
- if (mustFire)
- raiseHandler.Remove(n.item);
- }
-
- n = n.next;
- }
- }
-#else
- HashBag<T> toretain = new HashBag<T>(itemequalityComparer);
- toretain.AddAll(items);
- ViewHandler viewHandler = new ViewHandler(this);
- int index = 0, removed = 0, myoffset = Offset;
- Node node = startsentinel.next;
- while (node != endsentinel)
- {
- //Skip a stretch of nodes
- while (node != endsentinel && toretain.Remove(node.item))
- {
- node = node.next;
- index++;
- }
- viewHandler.skipEndpoints(removed, myoffset + index);
- //Remove a stretch of nodes
- Node localend = node.prev; //Latest node not to be removed
- while (node != endsentinel && !toretain.Contains(node.item))
- {
- if (mustFire)
- raiseHandler.Remove(node.item);
- removed++;
- node = node.next;
- index++;
- viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
- }
- viewHandler.updateSentinels(myoffset + index, localend, node);
- localend.next = node;
- node.prev = localend;
- }
- index = underlying != null ? underlying.size + 1 - myoffset : size + 1 - myoffset;
- viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
- size -= removed;
- if (underlying != null)
- underlying.size -= removed;
-#endif
- raiseHandler.Raise();
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="predicate"></param>
- void RetainAll(Fun<T, bool> predicate)
- {
- updatecheck();
- if (size == 0)
- return;
- RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this);
- bool mustFire = raiseHandler.MustFire;
-#if HASHINDEX
- {
- Node n = startsentinel.next;
-
- while (n != endsentinel)
- {
- bool removeIt = !predicate(n.item);
- updatecheck();
- if (removeIt)
- {
- dict.Remove(n.item);
- remove(n, 119);
- if (mustFire)
- raiseHandler.Remove(n.item);
- }
-
- n = n.next;
- }
- }
-#else
- ViewHandler viewHandler = new ViewHandler(this);
- int index = 0, removed = 0, myoffset = Offset;
- Node node = startsentinel.next;
- while (node != endsentinel)
- {
- //Skip a stretch of nodes
- while (node != endsentinel && predicate(node.item))
- {
- updatecheck();
- node = node.next;
- index++;
- }
- updatecheck();
- viewHandler.skipEndpoints(removed, myoffset + index);
- //Remove a stretch of nodes
- Node localend = node.prev; //Latest node not to be removed
- while (node != endsentinel && !predicate(node.item))
- {
- updatecheck();
- if (mustFire)
- raiseHandler.Remove(node.item);
- removed++;
- node = node.next;
- index++;
- viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
- }
- updatecheck();
- viewHandler.updateSentinels(myoffset + index, localend, node);
- localend.next = node;
- node.prev = localend;
- }
- index = underlying != null ? underlying.size + 1 - myoffset : size + 1 - myoffset;
- viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
- size -= removed;
- if (underlying != null)
- underlying.size -= removed;
-#endif
- raiseHandler.Raise();
- }
-
- /// <summary>
- /// Check if this collection contains all the values in another collection
- /// with respect to multiplicities.
- /// </summary>
- /// <param name="items">The </param>
- /// <typeparam name="U"></typeparam>
- /// <returns>True if all values in <code>items</code>is in this collection.</returns>
- [Tested]
- public virtual bool ContainsAll<U>(SCG.IEnumerable<U> items) where U : T
- {
- validitycheck();
-#if HASHINDEX
- Node node;
- foreach (T item in items)
- if (!contains(item, out node))
- return false;
- return true;
-#else
- HashBag<T> tocheck = new HashBag<T>(itemequalityComparer);
- tocheck.AddAll(items);
- if (tocheck.Count > size)
- return false;
- Node node = startsentinel.next;
- while (node != endsentinel)
- {
- tocheck.Remove(node.item);
- node = node.next;
- }
- return tocheck.IsEmpty;
-#endif
- }
-
-
- /// <summary>
- /// Create a new list consisting of the items of this list satisfying a
- /// certain predicate.
- /// </summary>
- /// <param name="filter">The filter delegate defining the predicate.</param>
- /// <returns>The new list.</returns>
- [Tested]
- public IList<T> FindAll(Fun<T, bool> filter)
- {
- validitycheck();
- int stamp = this.stamp;
- LinkedList<T> retval = new LinkedList<T>();
- Node cursor = startsentinel.next;
- Node mcursor = retval.startsentinel;
-#if HASHINDEX
- double tagdelta = int.MaxValue / (size + 1.0);
- int count = 1;
- TagGroup taggroup = new TagGroup();
- retval.taggroups = 1;
-#endif
- while (cursor != endsentinel)
- {
- bool found = filter(cursor.item);
- modifycheck(stamp);
- if (found)
- {
- mcursor.next = new Node(cursor.item, mcursor, null);
- mcursor = mcursor.next;
- retval.size++;
-#if HASHINDEX
- retval.dict.Add(cursor.item, mcursor);
- mcursor.taggroup = taggroup;
- mcursor.tag = (int)(tagdelta * count++);
-#endif
- }
- cursor = cursor.next;
- }
-#if HASHINDEX
- if (retval.size > 0)
- {
- taggroup.count = retval.size;
- taggroup.first = retval.startsentinel.next;
- taggroup.last = mcursor;
- }
-#endif
- retval.endsentinel.prev = mcursor;
- mcursor.next = retval.endsentinel;
- return retval;
- }
-
-
- /// <summary>
- /// Count the number of items of the collection equal to a particular value.
- /// Returns 0 if and only if the value is not in the collection.
- /// </summary>
- /// <param name="item">The value to count.</param>
- /// <returns>The number of copies found.</returns>
- [Tested]
- public virtual int ContainsCount(T item)
- {
-#if HASHINDEX
- return Contains(item) ? 1 : 0;
-#else
- validitycheck();
- int retval = 0;
- Node node = startsentinel.next;
- while (node != endsentinel)
- {
- if (itemequalityComparer.Equals(node.item, item))
- retval++;
- node = node.next;
- }
- return retval;
-#endif
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public virtual ICollectionValue<T> UniqueItems()
- {
-#if HASHINDEX
- return this;
-#else
- HashBag<T> hashbag = new HashBag<T>(itemequalityComparer);
- hashbag.AddAll(this);
- return hashbag.UniqueItems();
-#endif
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public virtual ICollectionValue<KeyValuePair<T, int>> ItemMultiplicities()
- {
-#if HASHINDEX
- return new MultiplicityOne<T>(this);
-#else
- HashBag<T> hashbag = new HashBag<T>(itemequalityComparer);
- hashbag.AddAll(this);
- return hashbag.ItemMultiplicities();
-#endif
- }
-
- /// <summary>
- /// Remove all items equivalent to a given value.
- /// <para>The asymptotic complexity of this method is <code>O(n+v*log(v))</code>,
- /// where <code>n</code> is the size of the collection and <code>v</code>
- /// is the number of views.
- /// </para>
- /// </summary>
- /// <param name="item">The value to remove.</param>
- [Tested]
- public virtual void RemoveAllCopies(T item)
- {
-#if HASHINDEX
- Remove(item);
-#else
- updatecheck();
- if (size == 0)
- return;
- RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this);
- bool mustFire = raiseHandler.MustFire;
- ViewHandler viewHandler = new ViewHandler(this);
- int index = 0, removed = 0, myoffset = Offset;
- //
- Node node = startsentinel.next;
- while (node != endsentinel)
- {
- //pass by a stretch of nodes
- while (node != endsentinel && !itemequalityComparer.Equals(node.item, item))
- {
- node = node.next;
- index++;
- }
- viewHandler.skipEndpoints(removed, myoffset + index);
- //Remove a stretch of nodes
- Node localend = node.prev; //Latest node not to be removed
- while (node != endsentinel && itemequalityComparer.Equals(node.item, item))
- {
- if (mustFire)
- raiseHandler.Remove(node.item);
- removed++;
- node = node.next;
- index++;
- viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
- }
- viewHandler.updateSentinels(myoffset + index, localend, node);
- localend.next = node;
- node.prev = localend;
- }
- index = underlying != null ? underlying.size + 1 - myoffset : size + 1 - myoffset;
- viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
- size -= removed;
- if (underlying != null)
- underlying.size -= removed;
- raiseHandler.Raise();
-#endif
- }
-
- #endregion
-
- #region ICollectionValue<T> Members
-
- /// <summary>
- ///
- /// </summary>
- /// <value>The number of items in this collection</value>
- [Tested]
- public override int Count { [Tested]get { validitycheck(); return size; } }
-
- /// <summary>
- /// Choose some item of this collection.
- /// </summary>
- /// <exception cref="NoSuchItemException">if collection is empty.</exception>
- /// <returns></returns>
- [Tested]
- public override T Choose() { return First; }
-
- /// <summary>
- /// Create an enumerable, enumerating the items of this collection that satisfies
- /// a certain condition.
- /// </summary>
- /// <param name="filter">The T->bool filter delegate defining the condition</param>
- /// <returns>The filtered enumerable</returns>
- public override SCG.IEnumerable<T> Filter(Fun<T, bool> filter) { validitycheck(); return base.Filter(filter); }
-
- #endregion
-
- #region IEnumerable<T> Members
- /// <summary>
- /// Create an enumerator for the collection
- /// </summary>
- /// <returns>The enumerator</returns>
- [Tested]
- public override SCG.IEnumerator<T> GetEnumerator()
- {
- validitycheck();
- Node cursor = startsentinel.next;
- int enumeratorstamp = underlying != null ? underlying.stamp : this.stamp;
-
- while (cursor != endsentinel)
- {
- modifycheck(enumeratorstamp);
- yield return cursor.item;
- cursor = cursor.next;
- }
- }
-
- #endregion
-
- #region IExtensible<T> Members
- /// <summary>
- /// Add an item to this collection if possible.
- /// </summary>
- /// <param name="item">The item to add.</param>
- /// <returns>True.</returns>
- [Tested]
- public virtual bool Add(T item)
- {
- updatecheck();
-#if HASHINDEX
- Node node = new Node(item);
- if (!dict.FindOrAdd(item, ref node))
- {
- insertNode(true, endsentinel, node);
- (underlying ?? this).raiseForAdd(item);
- return true;
- }
- return false;
-#else
- insert(size, endsentinel, item);
- (underlying ?? this).raiseForAdd(item);
- return true;
-#endif
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <value>True since this collection has bag semantics.</value>
- [Tested]
- public virtual bool AllowsDuplicates
- {
- [Tested]
- get
- {
-#if HASHINDEX
- return false;
-#else
- return true;
-#endif
- }
- }
-
- /// <summary>
- /// By convention this is true for any collection with set semantics.
- /// </summary>
- /// <value>True if only one representative of a group of equal items
- /// is kept in the collection together with the total count.</value>
- public virtual bool DuplicatesByCounting
- {
- get
- {
-#if HASHINDEX
- return true;
-#else
- return false;
-#endif
- }
- }
-
- /// <summary>
- /// Add the elements from another collection with a more specialized item type
- /// to this collection.
- /// </summary>
- /// <typeparam name="U">The type of items to add</typeparam>
- /// <param name="items">The items to add</param>
- [Tested]
- public virtual void AddAll<U>(SCG.IEnumerable<U> items) where U : T
- {
-#if HASHINDEX
- updatecheck();
- int added = 0;
- Node pred = endsentinel.prev;
- foreach (U item in items)
- {
- Node node = new Node(item);
- if (!dict.FindOrAdd(item, ref node))
- {
- insertNode(false, endsentinel, node);
- added++;
- }
- }
- if (added > 0)
- {
- fixViewsAfterInsert(endsentinel, pred, added, 0);
- raiseForInsertAll(pred, size - added, added, false);
- }
-#else
- insertAll(size, items, false);
-#endif
- }
-
- #endregion
-
-#if HASHINDEX
-#else
- #region IStack<T> Members
-
- /// <summary>
- /// Push an item to the top of the stack.
- /// </summary>
- /// <param name="item">The item</param>
- [Tested]
- public void Push(T item)
- {
- InsertLast(item);
- }
-
- /// <summary>
- /// Pop the item at the top of the stack from the stack.
- /// </summary>
- /// <returns>The popped item.</returns>
- [Tested]
- public T Pop()
- {
- return RemoveLast();
- }
-
- #endregion
-
- #region IQueue<T> Members
-
- /// <summary>
- /// Enqueue an item at the back of the queue.
- /// </summary>
- /// <param name="item">The item</param>
- [Tested]
- public virtual void Enqueue(T item)
- {
- InsertLast(item);
- }
-
- /// <summary>
- /// Dequeue an item from the front of the queue.
- /// </summary>
- /// <returns>The item</returns>
- [Tested]
- public virtual T Dequeue()
- {
- return RemoveFirst();
- }
- #endregion
-#endif
-
- #region Diagnostic
-
- private bool checkViews()
- {
- if (underlying != null)
- throw new InternalException(System.Reflection.MethodInfo.GetCurrentMethod() + " called on a view");
- if (views == null)
- return true;
- bool retval = true;
-
- Node[] nodes = new Node[size + 2];
- int i = 0;
- Node n = startsentinel;
- while (n != null)
- {
- nodes[i++] = n;
- n = n.next;
- }
- //Console.WriteLine("###");
- foreach (LinkedList<T> view in views)
- {
- if (!view.isValid)
- {
- Console.WriteLine("Invalid view(hash {0}, offset {1}, size {2})",
- view.GetHashCode(), view.offset, view.size);
- retval = false;
- continue;
- }
- if (view.Offset > size || view.Offset < 0)
- {
- Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), Offset > underlying.size ({2})",
- view.GetHashCode(), view.offset, view.size, size);
- retval = false;
- }
- else if (view.startsentinel != nodes[view.Offset])
- {
- Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), startsentinel {3} should be {4}",
- view.GetHashCode(), view.offset, view.size,
- view.startsentinel + " " + view.startsentinel.GetHashCode(),
- nodes[view.Offset] + " " + nodes[view.Offset].GetHashCode());
- retval = false;
- }
- if (view.Offset + view.size > size || view.Offset + view.size < 0)
- {
- Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), end index > underlying.size ({3})",
- view.GetHashCode(), view.offset, view.size, size);
- retval = false;
- }
- else if (view.endsentinel != nodes[view.Offset + view.size + 1])
- {
- Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), endsentinel {3} should be {4}",
- view.GetHashCode(), view.offset, view.size,
- view.endsentinel + " " + view.endsentinel.GetHashCode(),
- nodes[view.Offset + view.size + 1] + " " + nodes[view.Offset + view.size + 1].GetHashCode());
- retval = false;
- }
- if (view.views != views)
- {
- Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), wrong views list {3} <> {4}",
- view.GetHashCode(), view.offset, view.size, view.views.GetHashCode(), views.GetHashCode());
- retval = false;
- }
- if (view.underlying != this)
- {
- Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), wrong underlying {3} <> this {4}",
- view.GetHashCode(), view.offset, view.size, view.underlying.GetHashCode(), GetHashCode());
- retval = false;
- }
- if (view.stamp != stamp)
- {
- //Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), wrong stamp view:{2} underlying: {3}", view.GetHashCode(),view.offset, view.size, view.stamp, stamp);
- //retval = false;
- }
- }
- return retval;
- }
-
- string zeitem(Node node)
- {
- return node == null ? "(null node)" : node.item.ToString();
- }
-
- /// <summary>
- /// Check the sanity of this list
- /// </summary>
- /// <returns>true if sane</returns>
- [Tested]
- public virtual bool Check()
- {
- bool retval = true;
-
- /*if (underlying != null && underlying.stamp != stamp)
- {
- Console.WriteLine("underlying != null && underlying.stamp({0}) != stamp({1})", underlying.stamp, stamp);
- retval = false;
- }*/
-
- if (underlying != null)
- {
- //TODO: check that this view is included in viewsEndpoints tree
- return underlying.Check();
- }
-
- if (startsentinel == null)
- {
- Console.WriteLine("startsentinel == null");
- retval = false;
- }
-
- if (endsentinel == null)
- {
- Console.WriteLine("endsentinel == null");
- retval = false;
- }
-
- if (size == 0)
- {
- if (startsentinel != null && startsentinel.next != endsentinel)
- {
- Console.WriteLine("size == 0 but startsentinel.next != endsentinel");
- retval = false;
- }
-
- if (endsentinel != null && endsentinel.prev != startsentinel)
- {
- Console.WriteLine("size == 0 but endsentinel.prev != startsentinel");
- retval = false;
- }
- }
-
- if (startsentinel == null)
- {
- Console.WriteLine("NULL startsentinel");
- return retval;
- }
-
- int count = 0;
- Node node = startsentinel.next, prev = startsentinel;
-#if HASHINDEX
- int taggroupsize = 0, oldtaggroupsize = losize + 1, seentaggroups = 0;
- TagGroup oldtg = null;
-
- if (underlying == null)
- {
- TagGroup tg = startsentinel.taggroup;
-
- if (tg.count != 0 || tg.first != null || tg.last != null || tg.tag != int.MinValue)
- {
- Console.WriteLine("Bad startsentinel tag group: {0}", tg);
- retval = false;
- }
-
- tg = endsentinel.taggroup;
- if (tg.count != 0 || tg.first != null || tg.last != null || tg.tag != int.MaxValue)
- {
- Console.WriteLine("Bad endsentinel tag group: {0}", tg);
- retval = false;
- }
- }
-#endif
- while (node != endsentinel)
- {
- count++;
- if (node.prev != prev)
- {
- Console.WriteLine("Bad backpointer at node {0}", count);
- retval = false;
- }
-#if HASHINDEX
- if (underlying == null)
- {
- if (!node.prev.precedes(node))
- {
- Console.WriteLine("node.prev.tag ({0}, {1}) >= node.tag ({2}, {3}) at index={4} item={5} ", node.prev.taggroup.tag, node.prev.tag, node.taggroup.tag, node.tag, count, node.item);
- retval = false;
- }
-
- if (node.taggroup != oldtg)
- {
-
- if (node.taggroup.first != node)
- {
- string ntfi = zeitem(node.taggroup.first);
- Console.WriteLine("Bad first pointer in taggroup: node.taggroup.first.item ({0}), node.item ({1}) at index={2} item={3}", ntfi, node.item, count, node.item);
- retval = false;
- }
-
- if (oldtg != null)
- {
- if (oldtg.count != taggroupsize)
- {
- Console.WriteLine("Bad taggroupsize: oldtg.count ({0}) != taggroupsize ({1}) at index={2} item={3}", oldtg.count, taggroupsize, count, node.item);
- retval = false;
- }
-
- if (oldtaggroupsize <= losize && taggroupsize <= losize)
- {
- Console.WriteLine("Two small taggroups in a row: oldtaggroupsize ({0}), taggroupsize ({1}) at index={2} item={3}", oldtaggroupsize, taggroupsize, count, node.item);
- retval = false;
- }
-
- if (node.taggroup.tag <= oldtg.tag)
- {
- Console.WriteLine("Taggroup tags not strictly increasing: oldtaggrouptag ({0}), taggrouptag ({1}) at index={2} item={3}", oldtg.tag, node.taggroup.tag, count, node.item);
- retval = false;
- }
-
- if (oldtg.last != node.prev)
- {
- Console.WriteLine("Bad last pointer in taggroup: oldtg.last.item ({0}), node.prev.item ({1}) at index={2} item={3}", oldtg.last.item, node.prev.item, count, node.item);
- retval = false;
- }
-
- oldtaggroupsize = taggroupsize;
- }
-
- seentaggroups++;
- oldtg = node.taggroup;
- taggroupsize = 1;
- }
- else
- {
- taggroupsize++;
- }
- }
-
-#endif
- prev = node;
- node = node.next;
- if (node == null)
- {
- Console.WriteLine("Null next pointer at node {0}", count);
- return false;
- }
- }
-
-#if HASHINDEX
- if (underlying == null && size == 0 && taggroups != 0)
- {
- Console.WriteLine("Bad taggroups for empty list: size={0} taggroups={1}", size, taggroups);
- retval = false;
- }
- if (underlying == null && size > 0)
- {
- oldtg = node.prev.taggroup;
- if (oldtg != null)
- {
- if (oldtg.count != taggroupsize)
- {
- Console.WriteLine("Bad taggroupsize: oldtg.count ({0}) != taggroupsize ({1}) at index={2} item={3}", oldtg.count, taggroupsize, count, node.item);
- retval = false;
- }
-
- if (oldtaggroupsize <= losize && taggroupsize <= losize)
- {
- Console.WriteLine("Two small taggroups in a row: oldtaggroupsize ({0}), taggroupsize ({1}) at index={2} item={3}", oldtaggroupsize, taggroupsize, count, node.item);
- retval = false;
- }
-
- if (node.taggroup.tag <= oldtg.tag)
- {
- Console.WriteLine("Taggroup tags not strictly increasing: oldtaggrouptag ({0}), taggrouptag ({1}) at index={2} item={3}", oldtg.tag, node.taggroup.tag, count, node.item);
- retval = false;
- }
-
- if (oldtg.last != node.prev)
- {
- Console.WriteLine("Bad last pointer in taggroup: oldtg.last.item ({0}), node.prev.item ({1}) at index={2} item={3}", zeitem(oldtg.last), zeitem(node.prev), count, node.item);
- retval = false;
- }
- }
-
- if (seentaggroups != taggroups)
- {
- Console.WriteLine("seentaggroups ({0}) != taggroups ({1}) (at size {2})", seentaggroups, taggroups, size);
- retval = false;
- }
- }
-#endif
- if (count != size)
- {
- Console.WriteLine("size={0} but enumeration gives {1} nodes ", size, count);
- retval = false;
- }
-
- retval = checkViews() && retval;
-
-#if HASHINDEX
- if (!retval)
- return false;
- if (underlying == null)
- {
- if (size != dict.Count)
- {
- Console.WriteLine("list.size ({0}) != dict.Count ({1})", size, dict.Count);
- retval = false;
- }
- Node n = startsentinel.next, n2;
- while (n != endsentinel)
- {
- if (!dict.Find(n.item, out n2))
- {
- Console.WriteLine("Item in list but not dict: {0}", n.item);
- retval = false;
- }
- else if (n != n2)
- {
- Console.WriteLine("Wrong node in dict for item: {0}", n.item);
- retval = false;
- }
- n = n.next;
- }
- }
-#endif
- return retval;
- }
- #endregion
-
- #region ICloneable Members
-
- /// <summary>
- /// Make a shallow copy of this LinkedList.
- /// </summary>
- /// <returns></returns>
- public virtual object Clone()
- {
- LinkedList<T> clone = new LinkedList<T>(itemequalityComparer);
- clone.AddAll(this);
- return clone;
- }
-
- #endregion
-
- #region System.Collections.Generic.IList<T> Members
-
- void System.Collections.Generic.IList<T>.RemoveAt(int index)
- {
- RemoveAt(index);
- }
-
- void System.Collections.Generic.ICollection<T>.Add(T item)
- {
- Add(item);
- }
-
- #endregion
-
- #region System.Collections.ICollection Members
-
- bool System.Collections.ICollection.IsSynchronized
- {
- get { return false; }
- }
-
- [Obsolete]
- Object System.Collections.ICollection.SyncRoot
- {
- // Presumably safe to use the startsentinel (of type Node, always != null) as SyncRoot
- // since the class Node is private.
- get { return underlying != null ? ((System.Collections.ICollection)underlying).SyncRoot : startsentinel; }
- }
-
- void System.Collections.ICollection.CopyTo(Array arr, int index)
- {
- if (index < 0 || index + Count > arr.Length)
- throw new ArgumentOutOfRangeException();
-
- foreach (T item in this)
- arr.SetValue(item, index++);
- }
-
- #endregion
-
- #region System.Collections.IList Members
-
- Object System.Collections.IList.this[int index]
- {
- get { return this[index]; }
- set { this[index] = (T)value; }
- }
-
- int System.Collections.IList.Add(Object o)
- {
- bool added = Add((T)o);
- // What position to report if item not added? SC.IList.Add doesn't say
- return added ? Count-1 : -1;
- }
-
- bool System.Collections.IList.Contains(Object o)
- {
- return Contains((T)o);
- }
-
- int System.Collections.IList.IndexOf(Object o)
- {
- return Math.Max(-1, IndexOf((T)o));
- }
-
- void System.Collections.IList.Insert(int index, Object o)
- {
- Insert(index, (T)o);
- }
-
- void System.Collections.IList.Remove(Object o)
- {
- Remove((T)o);
- }
-
- void System.Collections.IList.RemoveAt(int index)
- {
- RemoveAt(index);
- }
-
- #endregion
- }
+/* + Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#define HASHINDEXnot + +using System; +using System.Diagnostics; +using SCG = System.Collections.Generic; + +namespace C5 +{ + /// <summary> + /// A list collection class based on a doubly linked list data structure. + /// </summary> + [Serializable] + public class LinkedList<T> : SequencedBase<T>, IList<T>, SCG.IList<T> +#if HASHINDEX +#else +, IStack<T>, IQueue<T> +#endif + { + #region Fields + /// <summary> + /// IExtensible.Add(T) always does AddLast(T), fIFO determines + /// if T Remove() does RemoveFirst() or RemoveLast() + /// </summary> + bool fIFO = true; + + #region Events + + /// <summary> + /// + /// </summary> + /// <value></value> + public override EventTypeEnum ListenableEvents { get { return underlying == null ? EventTypeEnum.All : EventTypeEnum.None; } } + + #endregion + + //Invariant: startsentinel != null && endsentinel != null + //If size==0: startsentinel.next == endsentinel && endsentinel.prev == startsentinel + //Else: startsentinel.next == First && endsentinel.prev == Last) + /// <summary> + /// Node to the left of first node + /// </summary> + Node startsentinel; + /// <summary> + /// Node to the right of last node + /// </summary> + Node endsentinel; + /// <summary> + /// Offset of this view in underlying list + /// </summary> +#if HASHINDEX + int? offset; +#else + int offset; +#endif + + /// <summary> + /// underlying list of this view (or null for the underlying list) + /// </summary> + LinkedList<T> underlying; + + //Note: all views will have the same views list since all view objects are created by MemberwiseClone() + WeakViewList<LinkedList<T>> views; + WeakViewList<LinkedList<T>>.Node myWeakReference; + + /// <summary> + /// Has this list or view not been invalidated by some operation (by someone calling Dispose()) + /// </summary> + bool isValid = true; + + +#if HASHINDEX + HashDictionary<T, Node> dict; + /// <summary> + /// Number of taggroups + /// </summary> + int taggroups; + /// <summary> + /// + /// </summary> + /// <value></value> + int Taggroups + { + get { return underlying == null ? taggroups : underlying.taggroups; } + set { if (underlying == null) taggroups = value; else underlying.taggroups = value; } + } +#endif + + #endregion + + #region Util + + bool equals(T i1, T i2) { return itemequalityComparer.Equals(i1, i2); } + + #region Check utilities + /// <summary> + /// Check if it is valid to perform updates and increment stamp of + /// underlying if this is a view. + /// <para>This method should be called in every public modifying + /// methods before any modifications are performed. + /// </para> + /// </summary> + /// <exception cref="InvalidOperationException"> if check fails.</exception> + protected override void updatecheck() + { + validitycheck(); + base.updatecheck(); + if (underlying != null) + underlying.stamp++; + } + + /// <summary> + /// Check if we are a view that the underlyinglist has only been updated through us. + /// <br/> + /// This method should be called from enumerators etc to guard against + /// modification of the base collection. + /// </summary> + /// <exception cref="InvalidOperationException"> if check fails.</exception> + void validitycheck() + { + if (!isValid) + throw new ViewDisposedException(); + } + + /// <summary> + /// Check that the list has not been updated since a particular time. + /// </summary> + /// <param name="stamp">The stamp indicating the time.</param> + /// <exception cref="CollectionModifiedException"> if check fails.</exception> + protected override void modifycheck(int stamp) + { + validitycheck(); + if ((underlying != null ? underlying.stamp : this.stamp) != stamp) + throw new CollectionModifiedException(); + } + #endregion + + #region Searching + bool contains(T item, out Node node) + { +#if HASHINDEX + if (dict.Find(item, out node)) + return insideview(node); +#else + //TODO: search from both ends? Or search from the end selected by FIFO? + node = startsentinel.next; + while (node != endsentinel) + { + if (equals(item, node.item)) + return true; + node = node.next; + } +#endif + return false; + } + + /// <summary> + /// Search forwards from a node for a node with a particular item. + /// </summary> + /// <param name="item">The item to look for</param> + /// <param name="node">On input, the node to start at. If item was found, the node found on output.</param> + /// <param name="index">If node was found, the value will be the number of links followed higher than + /// the value on input. If item was not found, the value on output is undefined.</param> + /// <returns>True if node was found.</returns> + bool find(T item, ref Node node, ref int index) + { + while (node != endsentinel) + { + //if (item.Equals(node.item)) + if (itemequalityComparer.Equals(item, node.item)) + return true; + + index++; + node = node.next; + } + + return false; + } + + bool dnif(T item, ref Node node, ref int index) + { + while (node != startsentinel) + { + //if (item.Equals(node.item)) + if (itemequalityComparer.Equals(item, node.item)) + return true; + + index--; + node = node.prev; + } + + return false; + } + +#if HASHINDEX + bool insideview(Node node) + { + if (underlying == null) + return true; + return (startsentinel.precedes(node) && node.precedes(endsentinel)); + } +#endif + + #endregion + + #region Indexing + /// <summary> + /// Return the node at position pos + /// </summary> + /// <param name="pos"></param> + /// <returns></returns> + Node get(int pos) + { + if (pos < 0 || pos >= size) + throw new IndexOutOfRangeException(); + else if (pos < size / 2) + { // Closer to front + Node node = startsentinel; + + for (int i = 0; i <= pos; i++) + node = node.next; + + return node; + } + else + { // Closer to end + Node node = endsentinel; + + for (int i = size; i > pos; i--) + node = node.prev; + + return node; + } + } + + /// <summary> + /// Find the distance from pos to the set given by positions. Return the + /// signed distance as return value and as an out parameter, the + /// array index of the nearest position. This is used for up to length 5 of + /// positions, and we do not assume it is sorted. + /// </summary> + /// <param name="pos"></param> + /// <param name="positions"></param> + /// <param name="nearest"></param> + /// <returns></returns> + int dist(int pos, out int nearest, int[] positions) + { + nearest = -1; + int bestdist = int.MaxValue; + int signeddist = bestdist; + for (int i = 0; i < positions.Length; i++) + { + int thisdist = positions[i] - pos; + if (thisdist >= 0 && thisdist < bestdist) { nearest = i; bestdist = thisdist; signeddist = thisdist; } + if (thisdist < 0 && -thisdist < bestdist) { nearest = i; bestdist = -thisdist; signeddist = thisdist; } + } + return signeddist; + } + + /// <summary> + /// Find the node at position pos, given known positions of several nodes. + /// </summary> + /// <param name="pos"></param> + /// <param name="positions"></param> + /// <param name="nodes"></param> + /// <returns></returns> + Node get(int pos, int[] positions, Node[] nodes) + { + int nearest; + int delta = dist(pos, out nearest, positions); + Node node = nodes[nearest]; + if (delta > 0) + for (int i = 0; i < delta; i++) + node = node.prev; + else + for (int i = 0; i > delta; i--) + node = node.next; + return node; + } + + /// <summary> + /// Get nodes at positions p1 and p2, given nodes at several positions. + /// </summary> + /// <param name="p1"></param> + /// <param name="p2"></param> + /// <param name="n1"></param> + /// <param name="n2"></param> + /// <param name="positions"></param> + /// <param name="nodes"></param> + void getPair(int p1, int p2, out Node n1, out Node n2, int[] positions, Node[] nodes) + { + int nearest1, nearest2; + int delta1 = dist(p1, out nearest1, positions), d1 = delta1 < 0 ? -delta1 : delta1; + int delta2 = dist(p2, out nearest2, positions), d2 = delta2 < 0 ? -delta2 : delta2; + + if (d1 < d2) + { + n1 = get(p1, positions, nodes); + n2 = get(p2, new int[] { positions[nearest2], p1 }, new Node[] { nodes[nearest2], n1 }); + } + else + { + n2 = get(p2, positions, nodes); + n1 = get(p1, new int[] { positions[nearest1], p2 }, new Node[] { nodes[nearest1], n2 }); + } + } + #endregion + + #region Insertion +#if HASHINDEX + void insert(int index, Node succ, T item) + { + Node newnode = new Node(item); + if (dict.FindOrAdd(item, ref newnode)) + throw new DuplicateNotAllowedException("Item already in indexed list"); + insertNode(true, succ, newnode); + } + + /// <summary> + /// Insert a Node before another one. Unchecked version. + /// </summary> + /// <param name="succ">The successor to be</param> + /// <param name="newnode">Node to insert</param> + /// <param name="updateViews">update overlapping view in this call</param> + void insertNode(bool updateViews, Node succ, Node newnode) + { + newnode.next = succ; + Node pred = newnode.prev = succ.prev; + succ.prev.next = newnode; + succ.prev = newnode; + size++; + if (underlying != null) + underlying.size++; + settag(newnode); + if (updateViews) + fixViewsAfterInsert(succ, pred, 1, 0); + } +#else + /// <summary> + /// + /// </summary> + /// <param name="index">The index in this view</param> + /// <param name="succ"></param> + /// <param name="item"></param> + /// <returns></returns> + Node insert(int index, Node succ, T item) + { + Node newnode = new Node(item, succ.prev, succ); + succ.prev.next = newnode; + succ.prev = newnode; + size++; + if (underlying != null) + underlying.size++; + fixViewsAfterInsert(succ, newnode.prev, 1, Offset + index); + return newnode; + } +#endif + #endregion + + #region Removal + T remove(Node node, int index) + { + fixViewsBeforeSingleRemove(node, Offset + index); + node.prev.next = node.next; + node.next.prev = node.prev; + size--; + if (underlying != null) + underlying.size--; +#if HASHINDEX + removefromtaggroup(node); +#endif + return node.item; + } + +#if HASHINDEX + private bool dictremove(T item, out Node node) + { + if (underlying == null) + { + if (!dict.Remove(item, out node)) + return false; + } + else + { + //We cannot avoid calling dict twice - have to intersperse the listorder test! + if (!contains(item, out node)) + return false; + dict.Remove(item); + } + return true; + } +#endif + #endregion + + #region fixView utilities + /// <summary> + /// + /// </summary> + /// <param name="added">The actual number of inserted nodes</param> + /// <param name="pred">The predecessor of the inserted nodes</param> + /// <param name="succ">The successor of the added nodes</param> + /// <param name="realInsertionIndex"></param> + void fixViewsAfterInsert(Node succ, Node pred, int added, int realInsertionIndex) + { + if (views != null) + foreach (LinkedList<T> view in views) + { + if (view != this) + { +#if HASHINDEX + if (pred.precedes(view.startsentinel) || (view.startsentinel == pred && view.size > 0)) + view.offset += added; + if (view.startsentinel.precedes(pred) && succ.precedes(view.endsentinel)) + view.size += added; + if (view.startsentinel == pred && view.size > 0) + view.startsentinel = succ.prev; + if (view.endsentinel == succ) + view.endsentinel = pred.next; +#else + if (view.Offset == realInsertionIndex && view.size > 0) + view.startsentinel = succ.prev; + if (view.Offset + view.size == realInsertionIndex) + view.endsentinel = pred.next; + if (view.Offset < realInsertionIndex && view.Offset + view.size > realInsertionIndex) + view.size += added; + if (view.Offset > realInsertionIndex || (view.Offset == realInsertionIndex && view.size > 0)) + view.offset += added; +#endif + } + } + } + + void fixViewsBeforeSingleRemove(Node node, int realRemovalIndex) + { + if (views != null) + foreach (LinkedList<T> view in views) + { + if (view != this) + { +#if HASHINDEX + if (view.startsentinel.precedes(node) && node.precedes(view.endsentinel)) + view.size--; + if (!view.startsentinel.precedes(node)) + view.offset--; + if (view.startsentinel == node) + view.startsentinel = node.prev; + if (view.endsentinel == node) + view.endsentinel = node.next; +#else + if (view.offset - 1 == realRemovalIndex) + view.startsentinel = node.prev; + if (view.offset + view.size == realRemovalIndex) + view.endsentinel = node.next; + if (view.offset <= realRemovalIndex && view.offset + view.size > realRemovalIndex) + view.size--; + if (view.offset > realRemovalIndex) + view.offset--; +#endif + } + } + } + +#if HASHINDEX +#else + void fixViewsBeforeRemove(int start, int count, Node first, Node last) + { + int clearend = start + count - 1; + if (views != null) + foreach (LinkedList<T> view in views) + { + if (view == this) + continue; + int viewoffset = view.Offset, viewend = viewoffset + view.size - 1; + //sentinels + if (start < viewoffset && viewoffset - 1 <= clearend) + view.startsentinel = first.prev; + if (start <= viewend + 1 && viewend < clearend) + view.endsentinel = last.next; + //offsets and sizes + if (start < viewoffset) + { + if (clearend < viewoffset) + view.offset = viewoffset - count; + else + { + view.offset = start; + view.size = clearend < viewend ? viewend - clearend : 0; + } + } + else if (start <= viewend) + view.size = clearend <= viewend ? view.size - count : start - viewoffset; + } + } +#endif + + /// <summary> + /// + /// </summary> + /// <param name="otherView"></param> + /// <returns>The position of View(otherOffset, otherSize) wrt. this view</returns> + MutualViewPosition viewPosition(LinkedList<T> otherView) + { +#if HASHINDEX + Node otherstartsentinel = otherView.startsentinel, otherendsentinel = otherView.endsentinel, + first = startsentinel.next, last = endsentinel.prev, + otherfirst = otherstartsentinel.next, otherlast = otherendsentinel.prev; + if (last.precedes(otherfirst) || otherlast.precedes(first)) + return MutualViewPosition.NonOverlapping; + if (size == 0 || (otherstartsentinel.precedes(first) && last.precedes(otherendsentinel))) + return MutualViewPosition.Contains; + if (otherView.size == 0 || (startsentinel.precedes(otherfirst) && otherlast.precedes(endsentinel))) + return MutualViewPosition.ContainedIn; + return MutualViewPosition.Overlapping; +#else + int end = offset + size, otherOffset = otherView.offset, otherSize = otherView.size, otherEnd = otherOffset + otherSize; + if (otherOffset >= end || otherEnd <= offset) + return MutualViewPosition.NonOverlapping; + if (size == 0 || (otherOffset <= offset && end <= otherEnd)) + return MutualViewPosition.Contains; + if (otherSize == 0 || (offset <= otherOffset && otherEnd <= end)) + return MutualViewPosition.ContainedIn; + return MutualViewPosition.Overlapping; +#endif + } + + void disposeOverlappingViews(bool reverse) + { + if (views != null) + { + foreach (LinkedList<T> view in views) + { + if (view != this) + { + switch (viewPosition(view)) + { + case MutualViewPosition.ContainedIn: + if (reverse) + { } + else + view.Dispose(); + break; + case MutualViewPosition.Overlapping: + view.Dispose(); + break; + case MutualViewPosition.Contains: + case MutualViewPosition.NonOverlapping: + break; + } + } + } + } + } + + #endregion + + #endregion + + #region Constructors + + /// <summary> + /// Create a linked list with en external item equalityComparer + /// </summary> + /// <param name="itemequalityComparer">The external equalityComparer</param> + public LinkedList(SCG.IEqualityComparer<T> itemequalityComparer) + : base(itemequalityComparer) + { + offset = 0; + size = stamp = 0; + startsentinel = new Node(default(T)); + endsentinel = new Node(default(T)); + startsentinel.next = endsentinel; + endsentinel.prev = startsentinel; +#if HASHINDEX + //It is important that the sentinels are different: + startsentinel.taggroup = new TagGroup(); + startsentinel.taggroup.tag = int.MinValue; + startsentinel.taggroup.count = 0; + endsentinel.taggroup = new TagGroup(); + endsentinel.taggroup.tag = int.MaxValue; + endsentinel.taggroup.count = 0; + dict = new HashDictionary<T, Node>(itemequalityComparer); +#endif + } + + /// <summary> + /// Create a linked list with the natural item equalityComparer + /// </summary> + public LinkedList() : this(EqualityComparer<T>.Default) { } + + #endregion + + #region Node nested class + + /// <summary> + /// An individual cell in the linked list + /// </summary> + [Serializable] + class Node + { + public Node prev; + + public Node next; + + public T item; + + #region Tag support +#if HASHINDEX + internal int tag; + + internal TagGroup taggroup; + + internal bool precedes(Node that) + { + //Debug.Assert(taggroup != null, "taggroup field null"); + //Debug.Assert(that.taggroup != null, "that.taggroup field null"); + int t1 = taggroup.tag; + int t2 = that.taggroup.tag; + + return t1 < t2 ? true : t1 > t2 ? false : tag < that.tag; + } +#endif + #endregion + + [Tested] + internal Node(T item) { this.item = item; } + + [Tested] + internal Node(T item, Node prev, Node next) + { + this.item = item; this.prev = prev; this.next = next; + } + + public override string ToString() + { +#if HASHINDEX + return String.Format("Node: (item={0}, tag={1})", item, tag); +#else + return String.Format("Node(item={0})", item); +#endif + } + } + + #endregion + + #region Taggroup nested class and tag maintenance utilities +#if HASHINDEX + /// <summary> + /// A group of nodes with the same high tag. Purpose is to be + /// able to tell the sequence order of two nodes without having to scan through + /// the list. + /// </summary> + [Serializable] + class TagGroup + { + internal int tag, count; + + internal Node first, last; + + /// <summary> + /// Pretty print a tag group + /// </summary> + /// <returns>Formatted tag group</returns> + public override string ToString() + { return String.Format("TagGroup(tag={0}, cnt={1}, fst={2}, lst={3})", tag, count, first, last); } + } + + //Constants for tag maintenance + const int wordsize = 32; + + const int lobits = 3; + + const int hibits = lobits + 1; + + const int losize = 1 << lobits; + + const int hisize = 1 << hibits; + + const int logwordsize = 5; + + TagGroup gettaggroup(Node pred, Node succ, out int lowbound, out int highbound) + { + TagGroup predgroup = pred.taggroup, succgroup = succ.taggroup; + + if (predgroup == succgroup) + { + lowbound = pred.tag + 1; + highbound = succ.tag - 1; + return predgroup; + } + else if (predgroup.first != null) + { + lowbound = pred.tag + 1; + highbound = int.MaxValue; + return predgroup; + } + else if (succgroup.first != null) + { + lowbound = int.MinValue; + highbound = succ.tag - 1; + return succgroup; + } + else + { + lowbound = int.MinValue; + highbound = int.MaxValue; + return new TagGroup(); + } + } + + + /// <summary> + /// Put a tag on a node (already inserted in the list). Split taggroups and renumber as + /// necessary. + /// </summary> + /// <param name="node">The node to tag</param> + void settag(Node node) + { + Node pred = node.prev, succ = node.next; + TagGroup predgroup = pred.taggroup, succgroup = succ.taggroup; + + if (predgroup == succgroup) + { + node.taggroup = predgroup; + predgroup.count++; + if (pred.tag + 1 == succ.tag) + splittaggroup(predgroup); + else + node.tag = (pred.tag + 1) / 2 + (succ.tag - 1) / 2; + } + else if (predgroup.first != null) + { + node.taggroup = predgroup; + predgroup.last = node; + predgroup.count++; + if (pred.tag == int.MaxValue) + splittaggroup(predgroup); + else + node.tag = pred.tag / 2 + int.MaxValue / 2 + 1; + } + else if (succgroup.first != null) + { + node.taggroup = succgroup; + succgroup.first = node; + succgroup.count++; + if (succ.tag == int.MinValue) + splittaggroup(node.taggroup); + else + node.tag = int.MinValue / 2 + (succ.tag - 1) / 2; + } + else + { + Debug.Assert(Taggroups == 0); + + TagGroup newgroup = new TagGroup(); + + Taggroups = 1; + node.taggroup = newgroup; + newgroup.first = newgroup.last = node; + newgroup.count = 1; + return; + } + } + + + /// <summary> + /// Remove a node from its taggroup. + /// <br/> When this is called, node must already have been removed from the underlying list + /// </summary> + /// <param name="node">The node to remove</param> + void removefromtaggroup(Node node) + { + + TagGroup taggroup = node.taggroup; + + if (--taggroup.count == 0) + { + Taggroups--; + return; + } + + if (node == taggroup.first) + taggroup.first = node.next; + + if (node == taggroup.last) + taggroup.last = node.prev; + + //node.taggroup = null; + if (taggroup.count != losize || Taggroups == 1) + return; + + TagGroup otg; + // bug20070911: + Node neighbor; + if ((neighbor = taggroup.first.prev) != startsentinel + && (otg = neighbor.taggroup).count <= losize) + taggroup.first = otg.first; + else if ((neighbor = taggroup.last.next) != endsentinel + && (otg = neighbor.taggroup).count <= losize) + taggroup.last = otg.last; + else + return; + + Node n = otg.first; + + for (int i = 0, length = otg.count; i < length; i++) + { + n.taggroup = taggroup; + n = n.next; + } + + taggroup.count += otg.count; + Taggroups--; + n = taggroup.first; + + const int ofs = wordsize - hibits; + + for (int i = 0, count = taggroup.count; i < count; i++) + { + n.tag = (i - losize) << ofs; //(i-8)<<28 + n = n.next; + } + } + + + /// <summary> + /// Split a tag group to make rom for more tags. + /// </summary> + /// <param name="taggroup">The tag group</param> + void splittaggroup(TagGroup taggroup) + { + Node n = taggroup.first; + int ptgt = taggroup.first.prev.taggroup.tag; + int ntgt = taggroup.last.next.taggroup.tag; + + Debug.Assert(ptgt + 1 <= ntgt - 1); + + int ofs = wordsize - hibits; + int newtgs = (taggroup.count - 1) / hisize; + int tgtdelta = (int)((ntgt + 0.0 - ptgt) / (newtgs + 2)), tgtag = ptgt; + + tgtdelta = tgtdelta == 0 ? 1 : tgtdelta; + for (int j = 0; j < newtgs; j++) + { + TagGroup newtaggroup = new TagGroup(); + + newtaggroup.tag = (tgtag = tgtag >= ntgt - tgtdelta ? ntgt : tgtag + tgtdelta); + newtaggroup.first = n; + newtaggroup.count = hisize; + for (int i = 0; i < hisize; i++) + { + n.taggroup = newtaggroup; + n.tag = (i - losize) << ofs; //(i-8)<<28 + n = n.next; + } + + newtaggroup.last = n.prev; + } + + int rest = taggroup.count - hisize * newtgs; + + taggroup.first = n; + taggroup.count = rest; + taggroup.tag = (tgtag = tgtag >= ntgt - tgtdelta ? ntgt : tgtag + tgtdelta); ofs--; + for (int i = 0; i < rest; i++) + { + n.tag = (i - hisize) << ofs; //(i-16)<<27 + n = n.next; + } + + taggroup.last = n.prev; + Taggroups += newtgs; + if (tgtag == ntgt) + redistributetaggroups(taggroup); + } + + + private void redistributetaggroups(TagGroup taggroup) + { + TagGroup pred = taggroup, succ = taggroup, tmp; + double limit = 1, bigt = Math.Pow(Taggroups, 1.0 / 30);//????? + int bits = 1, count = 1, lowmask = 0, himask = 0, target = 0; + + do + { + bits++; + lowmask = (1 << bits) - 1; + himask = ~lowmask; + target = taggroup.tag & himask; + while ((tmp = pred.first.prev.taggroup).first != null && (tmp.tag & himask) == target) + { count++; pred = tmp; } + + while ((tmp = succ.last.next.taggroup).last != null && (tmp.tag & himask) == target) + { count++; succ = tmp; } + + limit *= bigt; + } while (count > limit); + + //redistibute tags + int lob = pred.first.prev.taggroup.tag, upb = succ.last.next.taggroup.tag; + int delta = upb / (count + 1) - lob / (count + 1); + + Debug.Assert(delta > 0); + for (int i = 0; i < count; i++) + { + pred.tag = lob + (i + 1) * delta; + pred = pred.last.next.taggroup; + } + } +#endif + + #endregion + + #region Position, PositionComparer and ViewHandler nested types + class PositionComparer : SCG.IComparer<Position> + { + static PositionComparer _default; + PositionComparer() { } + public static PositionComparer Default { get { return _default ?? (_default = new PositionComparer()); } } + public int Compare(Position a, Position b) + { +#if HASHINDEX + return a.Endpoint == b.Endpoint ? 0 : a.Endpoint.precedes(b.Endpoint) ? -1 : 1; +#else + return a.Index.CompareTo(b.Index); +#endif + } + } + /// <summary> + /// During RemoveAll, we need to cache the original endpoint indices of views + /// </summary> + struct Position + { + public readonly LinkedList<T> View; + public bool Left; +#if HASHINDEX + public readonly Node Endpoint; +#else + public readonly int Index; +#endif + public Position(LinkedList<T> view, bool left) + { + View = view; + Left = left; +#if HASHINDEX + Endpoint = left ? view.startsentinel.next : view.endsentinel.prev; +#else + Index = left ? view.Offset : view.Offset + view.size - 1; +#endif + } +#if HASHINDEX + public Position(Node node, int foo) { this.Endpoint = node; View = null; Left = false; } +#else + public Position(int index) { this.Index = index; View = null; Left = false; } +#endif + } + + //TODO: merge the two implementations using Position values as arguments + /// <summary> + /// Handle the update of (other) views during a multi-remove operation. + /// </summary> + struct ViewHandler + { + ArrayList<Position> leftEnds; + ArrayList<Position> rightEnds; + int leftEndIndex, rightEndIndex, leftEndIndex2, rightEndIndex2; + internal readonly int viewCount; + internal ViewHandler(LinkedList<T> list) + { + leftEndIndex = rightEndIndex = leftEndIndex2 = rightEndIndex2 = viewCount = 0; + leftEnds = rightEnds = null; + if (list.views != null) + foreach (LinkedList<T> v in list.views) + if (v != list) + { + if (leftEnds == null) + { + leftEnds = new ArrayList<Position>(); + rightEnds = new ArrayList<Position>(); + } + leftEnds.Add(new Position(v, true)); + rightEnds.Add(new Position(v, false)); + } + if (leftEnds == null) + return; + viewCount = leftEnds.Count; + leftEnds.Sort(PositionComparer.Default); + rightEnds.Sort(PositionComparer.Default); + } +#if HASHINDEX + internal void skipEndpoints(int removed, Node n) + { + if (viewCount > 0) + { + Position endpoint; + while (leftEndIndex < viewCount && ((endpoint = leftEnds[leftEndIndex]).Endpoint.prev.precedes(n))) + { + LinkedList<T> view = endpoint.View; + view.offset = view.offset - removed;//TODO: extract offset.Value? + view.size += removed; + leftEndIndex++; + } + while (rightEndIndex < viewCount && (endpoint = rightEnds[rightEndIndex]).Endpoint.precedes(n)) + { + LinkedList<T> view = endpoint.View; + view.size -= removed; + rightEndIndex++; + } + } + if (viewCount > 0) + { + Position endpoint; + while (leftEndIndex2 < viewCount && (endpoint = leftEnds[leftEndIndex2]).Endpoint.prev.precedes(n)) + leftEndIndex2++; + while (rightEndIndex2 < viewCount && (endpoint = rightEnds[rightEndIndex2]).Endpoint.next.precedes(n)) + rightEndIndex2++; + } + } + /// <summary> + /// To be called with n pointing to the right of each node to be removed in a stretch. + /// And at the endsentinel. + /// + /// Update offset of a view whose left endpoint (has not already been handled and) is n or precedes n. + /// I.e. startsentinel precedes n. + /// Also update the size as a prelude to handling the right endpoint. + /// + /// Update size of a view not already handled and whose right endpoint precedes n. + /// </summary> + /// <param name="removed">The number of nodes left of n to be removed</param> + /// <param name="n"></param> + internal void updateViewSizesAndCounts(int removed, Node n) + { + if (viewCount > 0) + { + Position endpoint; + while (leftEndIndex < viewCount && ((endpoint = leftEnds[leftEndIndex]).Endpoint.prev.precedes(n))) + { + LinkedList<T> view = endpoint.View; + view.offset = view.offset - removed; //TODO: fix use of offset + view.size += removed; + leftEndIndex++; + } + while (rightEndIndex < viewCount && (endpoint = rightEnds[rightEndIndex]).Endpoint.precedes(n)) + { + LinkedList<T> view = endpoint.View; + view.size -= removed; + rightEndIndex++; + } + } + } + /// <summary> + /// To be called with n being the first not-to-be-removed node after a (stretch of) node(s) to be removed. + /// + /// It will update the startsentinel of views (that have not been handled before and) + /// whose startsentinel precedes n, i.e. is to be deleted. + /// + /// It will update the endsentinel of views (...) whose endsentinel precedes n, i.e. is to be deleted. + /// + /// PROBLEM: DOESNT WORK AS ORIGINALLY ADVERTISED. WE MUST DO THIS BEFORE WE ACTUALLY REMOVE THE NODES. WHEN THE + /// NODES HAVE BEEN REMOVED, THE precedes METHOD WILL NOT WORK! + /// </summary> + /// <param name="n"></param> + /// <param name="newstart"></param> + /// <param name="newend"></param> + internal void updateSentinels(Node n, Node newstart, Node newend) + { + if (viewCount > 0) + { + Position endpoint; + while (leftEndIndex2 < viewCount && (endpoint = leftEnds[leftEndIndex2]).Endpoint.prev.precedes(n)) + { + LinkedList<T> view = endpoint.View; + view.startsentinel = newstart; + leftEndIndex2++; + } + while (rightEndIndex2 < viewCount && (endpoint = rightEnds[rightEndIndex2]).Endpoint.next.precedes(n)) + { + LinkedList<T> view = endpoint.View; + view.endsentinel = newend; + rightEndIndex2++; + } + } + } +#else + /// <summary> + /// This is to be called with realindex pointing to the first node to be removed after a (stretch of) node that was not removed + /// </summary> + /// <param name="removed"></param> + /// <param name="realindex"></param> + internal void skipEndpoints(int removed, int realindex) + { + if (viewCount > 0) + { + Position endpoint; + while (leftEndIndex < viewCount && (endpoint = leftEnds[leftEndIndex]).Index <= realindex) + { + LinkedList<T> view = endpoint.View; + view.offset = view.offset - removed; + view.size += removed; + leftEndIndex++; + } + while (rightEndIndex < viewCount && (endpoint = rightEnds[rightEndIndex]).Index < realindex) + { + LinkedList<T> view = endpoint.View; + view.size -= removed; + rightEndIndex++; + } + } + if (viewCount > 0) + { + Position endpoint; + while (leftEndIndex2 < viewCount && (endpoint = leftEnds[leftEndIndex2]).Index <= realindex) + leftEndIndex2++; + while (rightEndIndex2 < viewCount && (endpoint = rightEnds[rightEndIndex2]).Index < realindex - 1) + rightEndIndex2++; + } + } + internal void updateViewSizesAndCounts(int removed, int realindex) + { + if (viewCount > 0) + { + Position endpoint; + while (leftEndIndex < viewCount && (endpoint = leftEnds[leftEndIndex]).Index <= realindex) + { + LinkedList<T> view = endpoint.View; + view.offset = view.Offset - removed; + view.size += removed; + leftEndIndex++; + } + while (rightEndIndex < viewCount && (endpoint = rightEnds[rightEndIndex]).Index < realindex) + { + LinkedList<T> view = endpoint.View; + view.size -= removed; + rightEndIndex++; + } + } + } + internal void updateSentinels(int realindex, Node newstart, Node newend) + { + if (viewCount > 0) + { + Position endpoint; + while (leftEndIndex2 < viewCount && (endpoint = leftEnds[leftEndIndex2]).Index <= realindex) + { + LinkedList<T> view = endpoint.View; + view.startsentinel = newstart; + leftEndIndex2++; + } + while (rightEndIndex2 < viewCount && (endpoint = rightEnds[rightEndIndex2]).Index < realindex - 1) + { + LinkedList<T> view = endpoint.View; + view.endsentinel = newend; + rightEndIndex2++; + } + } + } +#endif + } + #endregion + + #region Range nested class + + class Range : DirectedCollectionValueBase<T>, IDirectedCollectionValue<T> + { + int start, count, rangestamp; + Node startnode, endnode; + + LinkedList<T> list; + + bool forwards; + + + internal Range(LinkedList<T> list, int start, int count, bool forwards) + { + this.list = list; this.rangestamp = list.underlying != null ? list.underlying.stamp : list.stamp; + this.start = start; this.count = count; this.forwards = forwards; + if (count > 0) + { + startnode = list.get(start); + endnode = list.get(start + count - 1); + } + } + + public override bool IsEmpty { get { list.modifycheck(rangestamp); return count == 0; } } + + [Tested] + public override int Count { [Tested]get { list.modifycheck(rangestamp); return count; } } + + + public override Speed CountSpeed { get { list.modifycheck(rangestamp); return Speed.Constant; } } + + + public override T Choose() + { + list.modifycheck(rangestamp); + if (count > 0) return startnode.item; + throw new NoSuchItemException(); + } + + + [Tested] + public override SCG.IEnumerator<T> GetEnumerator() + { + int togo = count; + + list.modifycheck(rangestamp); + if (togo == 0) + yield break; + + Node cursor = forwards ? startnode : endnode; + + yield return cursor.item; + while (--togo > 0) + { + cursor = forwards ? cursor.next : cursor.prev; + list.modifycheck(rangestamp); + yield return cursor.item; + } + } + + + [Tested] + public override IDirectedCollectionValue<T> Backwards() + { + list.modifycheck(rangestamp); + + Range b = (Range)MemberwiseClone(); + + b.forwards = !forwards; + return b; + } + + + [Tested] + IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return Backwards(); } + + + [Tested] + public override EnumerationDirection Direction + { + [Tested] + get + { return forwards ? EnumerationDirection.Forwards : EnumerationDirection.Backwards; } + } + } + + + #endregion + + #region IDisposable Members + + /// <summary> + /// Invalidate this list. If a view, just invalidate the view. + /// If not a view, invalidate the list and all views on it. + /// </summary> + public virtual void Dispose() + { + Dispose(false); + } + + void Dispose(bool disposingUnderlying) + { + if (isValid) + { + if (underlying != null) + { + isValid = false; + if (!disposingUnderlying && views != null) + views.Remove(myWeakReference); + endsentinel = null; + startsentinel = null; + underlying = null; + views = null; + myWeakReference = null; + } + else + { + //isValid = false; + //endsentinel = null; + //startsentinel = null; + if (views != null) + foreach (LinkedList<T> view in views) + view.Dispose(true); + //views = null; + Clear(); + } + } + } + + #endregion IDisposable stuff + + #region IList<T> Members + + /// <summary> + /// </summary> + /// <exception cref="NoSuchItemException"> if this list is empty.</exception> + /// <value>The first item in this list.</value> + [Tested] + public virtual T First + { + [Tested] + get + { + validitycheck(); + if (size == 0) + throw new NoSuchItemException(); + return startsentinel.next.item; + } + } + + + /// <summary> + /// </summary> + /// <exception cref="NoSuchItemException"> if this list is empty.</exception> + /// <value>The last item in this list.</value> + [Tested] + public virtual T Last + { + [Tested] + get + { + validitycheck(); + if (size == 0) + throw new NoSuchItemException(); + return endsentinel.prev.item; + } + } + + /// <summary> + /// Since <code>Add(T item)</code> always add at the end of the list, + /// this describes if list has FIFO or LIFO semantics. + /// </summary> + /// <value>True if the <code>Remove()</code> operation removes from the + /// start of the list, false if it removes from the end. THe default for a new linked list is true.</value> + [Tested] + public virtual bool FIFO + { + [Tested] + get { validitycheck(); return fIFO; } + [Tested] + set { updatecheck(); fIFO = value; } + } + + /// <summary> + /// + /// </summary> + public virtual bool IsFixedSize + { + get { validitycheck(); return false; } + } + + /// <summary> + /// On this list, this indexer is read/write. + /// <exception cref="IndexOutOfRangeException"/> if i is negative or + /// >= the size of the collection. + /// </summary> + /// <value>The i'th item of this list.</value> + /// <param name="index">The index of the item to fetch or store.</param> + [Tested] + public virtual T this[int index] + { + [Tested] + get { validitycheck(); return get(index).item; } + [Tested] + set + { + updatecheck(); + Node n = get(index); + // + T item = n.item; +#if HASHINDEX + + if (itemequalityComparer.Equals(value, item)) + { + n.item = value; + dict.Update(value, n); + } + else if (!dict.FindOrAdd(value, ref n)) + { + dict.Remove(item); + n.item = value; + } + else + throw new ArgumentException("Item already in indexed list"); +#else + n.item = value; +#endif + (underlying ?? this).raiseForSetThis(index, value, item); + } + } + + /// <summary> + /// + /// </summary> + /// <value></value> + public virtual Speed IndexingSpeed { get { return Speed.Linear; } } + + /// <summary> + /// Insert an item at a specific index location in this list. + /// <exception cref="IndexOutOfRangeException"/> if i is negative or + /// > the size of the collection.</summary> + /// <param name="i">The index at which to insert.</param> + /// <param name="item">The item to insert.</param> + [Tested] + public virtual void Insert(int i, T item) + { + updatecheck(); + insert(i, i == size ? endsentinel : get(i), item); + if (ActiveEvents != EventTypeEnum.None) + (underlying ?? this).raiseForInsert(i + Offset, item); + } + + /// <summary> + /// Insert an item at the end of a compatible view, used as a pointer. + /// <para>The <code>pointer</code> must be a view on the same list as + /// <code>this</code> and the endpoitn of <code>pointer</code> must be + /// a valid insertion point of <code>this</code></para> + /// </summary> + /// <exception cref="IncompatibleViewException">If <code>pointer</code> + /// is not a view on the same list as <code>this</code></exception> + /// <exception cref="IndexOutOfRangeException"><b>??????</b> if the endpoint of + /// <code>pointer</code> is not inside <code>this</code></exception> + /// <exception cref="DuplicateNotAllowedException"> if the list has + /// <code>AllowsDuplicates==false</code> and the item is + /// already in the list.</exception> + /// <param name="pointer"></param> + /// <param name="item"></param> + public void Insert(IList<T> pointer, T item) + { + updatecheck(); + if ((pointer == null) || ((pointer.Underlying ?? pointer) != (underlying ?? this))) + throw new IncompatibleViewException(); +#warning INEFFICIENT + //TODO: make this efficient (the whole point of the method: + //Do NOT use Insert, but insert the node at pointer.endsentinel, checking + //via the ordering that this is a valid insertion point + Insert(pointer.Offset + pointer.Count - Offset, item); + } + + /// <summary> + /// Insert into this list all items from an enumerable collection starting + /// at a particular index. + /// <exception cref="IndexOutOfRangeException"/> if i is negative or + /// > the size of the collection. + /// </summary> + /// <param name="i">Index to start inserting at</param> + /// <param name="items">Items to insert</param> + /// <typeparam name="U"></typeparam> + [Tested] + public virtual void InsertAll<U>(int i, SCG.IEnumerable<U> items) where U : T + { + insertAll(i, items, true); + } + + void insertAll<U>(int i, SCG.IEnumerable<U> items, bool insertion) where U : T + { + updatecheck(); + Node succ, node, pred; + int count = 0; + succ = i == size ? endsentinel : get(i); + pred = node = succ.prev; +#if HASHINDEX + TagGroup taggroup = null; + int taglimit = 0, thetag = 0; + taggroup = gettaggroup(node, succ, out thetag, out taglimit); + try + { + foreach (T item in items) + { + Node tmp = new Node(item, node, null); + if (!dict.FindOrAdd(item, ref tmp)) + { + tmp.tag = thetag < taglimit ? ++thetag : thetag; + tmp.taggroup = taggroup; + node.next = tmp; + count++; + node = tmp; + } + else + throw new DuplicateNotAllowedException("Item already in indexed list"); + } + } + finally + { + if (count != 0) + { + taggroup.count += count; + if (taggroup != pred.taggroup) + taggroup.first = pred.next; + if (taggroup != succ.taggroup) + taggroup.last = node; + succ.prev = node; + node.next = succ; + if (node.tag == node.prev.tag) + splittaggroup(taggroup); + size += count; + if (underlying != null) + underlying.size += count; + fixViewsAfterInsert(succ, pred, count, 0); + raiseForInsertAll(pred, i, count, insertion); + } + } +#else + foreach (T item in items) + { + Node tmp = new Node(item, node, null); + node.next = tmp; + count++; + node = tmp; + } + if (count == 0) + return; + succ.prev = node; + node.next = succ; + size += count; + if (underlying != null) + underlying.size += count; + if (count > 0) + { + fixViewsAfterInsert(succ, pred, count, offset + i); + raiseForInsertAll(pred, i, count, insertion); + } +#endif + } + + private void raiseForInsertAll(Node node, int i, int added, bool insertion) + { + if (ActiveEvents != 0) + { + int index = Offset + i; + if ((ActiveEvents & (EventTypeEnum.Added | EventTypeEnum.Inserted)) != 0) + for (int j = index; j < index + added; j++) + { +#warning must we check stamps here? + node = node.next; + T item = node.item; + if (insertion) raiseItemInserted(item, j); + raiseItemsAdded(item, 1); + } + raiseCollectionChanged(); + } + } + + /// <summary> + /// Insert an item at the front of this list. + /// </summary> + /// <param name="item">The item to insert.</param> + [Tested] + public virtual void InsertFirst(T item) + { + updatecheck(); + insert(0, startsentinel.next, item); + if (ActiveEvents != EventTypeEnum.None) + (underlying ?? this).raiseForInsert(0 + Offset, item); + } + + /// <summary> + /// Insert an item at the back of this list. + /// </summary> + /// <param name="item">The item to insert.</param> + [Tested] + public virtual void InsertLast(T item) + { + updatecheck(); + insert(size, endsentinel, item); + if (ActiveEvents != EventTypeEnum.None) + (underlying ?? this).raiseForInsert(size - 1 + Offset, item); + } + + /// <summary> + /// Create a new list consisting of the results of mapping all items of this + /// list. + /// </summary> + /// <param name="mapper">The delegate defining the map.</param> + /// <returns>The new list.</returns> + [Tested] + public IList<V> Map<V>(Fun<T, V> mapper) + { + validitycheck(); + + LinkedList<V> retval = new LinkedList<V>(); + return map<V>(mapper, retval); + } + + /// <summary> + /// Create a new list consisting of the results of mapping all items of this + /// list. The new list will use a specified equalityComparer for the item type. + /// </summary> + /// <typeparam name="V">The type of items of the new list</typeparam> + /// <param name="mapper">The delegate defining the map.</param> + /// <param name="equalityComparer">The equalityComparer to use for the new list</param> + /// <returns>The new list.</returns> + public IList<V> Map<V>(Fun<T, V> mapper, SCG.IEqualityComparer<V> equalityComparer) + { + validitycheck(); + + LinkedList<V> retval = new LinkedList<V>(equalityComparer); + return map<V>(mapper, retval); + } + + private IList<V> map<V>(Fun<T, V> mapper, LinkedList<V> retval) + { + if (size == 0) + return retval; + int stamp = this.stamp; + Node cursor = startsentinel.next; + LinkedList<V>.Node mcursor = retval.startsentinel; + +#if HASHINDEX + double tagdelta = int.MaxValue / (size + 1.0); + int count = 1; + LinkedList<V>.TagGroup taggroup = null; + taggroup = new LinkedList<V>.TagGroup(); + retval.taggroups = 1; + taggroup.count = size; +#endif + while (cursor != endsentinel) + { + V v = mapper(cursor.item); + modifycheck(stamp); + mcursor.next = new LinkedList<V>.Node(v, mcursor, null); + cursor = cursor.next; + mcursor = mcursor.next; +#if HASHINDEX + retval.dict.Add(v, mcursor); + mcursor.taggroup = taggroup; + mcursor.tag = (int)(tagdelta * count++); +#endif + } + +#if HASHINDEX + taggroup.first = retval.startsentinel.next; + taggroup.last = mcursor; +#endif + retval.endsentinel.prev = mcursor; + mcursor.next = retval.endsentinel; + retval.size = size; + return retval; + } + + /// <summary> + /// Remove one item from the list: from the front if <code>FIFO</code> + /// is true, else from the back. + /// <exception cref="NoSuchItemException"/> if this list is empty. + /// </summary> + /// <returns>The removed item.</returns> + [Tested] + public virtual T Remove() + { + updatecheck(); + if (size == 0) + throw new NoSuchItemException("List is empty"); + T item = fIFO ? remove(startsentinel.next, 0) : remove(endsentinel.prev, size - 1); +#if HASHINDEX + dict.Remove(item); +#endif + (underlying ?? this).raiseForRemove(item); + return item; + } + + /// <summary> + /// Remove one item from the front of the list. + /// <exception cref="NoSuchItemException"/> if this list is empty. + /// </summary> + /// <returns>The removed item.</returns> + [Tested] + public virtual T RemoveFirst() + { + updatecheck(); + if (size == 0) + throw new NoSuchItemException("List is empty"); + + T item = remove(startsentinel.next, 0); +#if HASHINDEX + dict.Remove(item); +#endif + if (ActiveEvents != EventTypeEnum.None) + (underlying ?? this).raiseForRemoveAt(Offset, item); + return item; + } + + /// <summary> + /// Remove one item from the back of the list. + /// <exception cref="NoSuchItemException"/> if this list is empty. + /// </summary> + /// <returns>The removed item.</returns> + [Tested] + public virtual T RemoveLast() + { + updatecheck(); + if (size == 0) + throw new NoSuchItemException("List is empty"); + + T item = remove(endsentinel.prev, size - 1); +#if HASHINDEX + dict.Remove(item); +#endif + if (ActiveEvents != EventTypeEnum.None) + (underlying ?? this).raiseForRemoveAt(size + Offset, item); + return item; + } + + /// <summary> + /// Create a list view on this list. + /// </summary> + /// <exception cref="ArgumentOutOfRangeException"> if the start or count is negative</exception> + /// <exception cref="ArgumentException"> if the range does not fit within list.</exception> + /// <param name="start">The index in this list of the start of the view.</param> + /// <param name="count">The size of the view.</param> + /// <returns>The new list view.</returns> + [Tested] + public virtual IList<T> View(int start, int count) + { + checkRange(start, count); + validitycheck(); + if (views == null) + views = new WeakViewList<LinkedList<T>>(); + LinkedList<T> retval = (LinkedList<T>)MemberwiseClone(); + retval.underlying = underlying != null ? underlying : this; + retval.offset = offset + start; + retval.size = count; + getPair(start - 1, start + count, out retval.startsentinel, out retval.endsentinel, + new int[] { -1, size }, new Node[] { startsentinel, endsentinel }); + //retval.startsentinel = start == 0 ? startsentinel : get(start - 1); + //retval.endsentinel = start + count == size ? endsentinel : get(start + count); + + //TODO: for the purpose of Dispose, we need to retain a ref to the node + retval.myWeakReference = views.Add(retval); + return retval; + } + + /// <summary> + /// Create a list view on this list containing the (first) occurrence of a particular item. + /// </summary> + /// <exception cref="ArgumentException"> if the item is not in this list.</exception> + /// <param name="item">The item to find.</param> + /// <returns>The new list view.</returns> + public virtual IList<T> ViewOf(T item) + { +#if HASHINDEX + Node n; + validitycheck(); + if (!contains(item, out n)) + return null; + LinkedList<T> retval = (LinkedList<T>)MemberwiseClone(); + retval.underlying = underlying != null ? underlying : this; + retval.offset = null; + retval.startsentinel = n.prev; + retval.endsentinel = n.next; + retval.size = 1; + return retval; +#else + int index = 0; + Node n = startsentinel.next; + if (!find(item, ref n, ref index)) + return null; + //TODO: optimize with getpair! + return View(index, 1); +#endif + } + + /// <summary> + /// Create a list view on this list containing the last occurrence of a particular item. + /// <exception cref="ArgumentException"/> if the item is not in this list. + /// </summary> + /// <param name="item">The item to find.</param> + /// <returns>The new list view.</returns> + public virtual IList<T> LastViewOf(T item) + { +#if HASHINDEX + return ViewOf(item); +#else + int index = size - 1; + Node n = endsentinel.prev; + if (!dnif(item, ref n, ref index)) + return null; + return View(index, 1); +#endif + } + + /// <summary> + /// Null if this list is not a view. + /// </summary> + /// <value>Underlying list for view.</value> + [Tested] + public virtual IList<T> Underlying { [Tested]get { validitycheck(); return underlying; } } + + /// <summary> + /// + /// </summary> + /// <value></value> + public virtual bool IsValid { get { return isValid; } } + + /// <summary> + /// </summary> + /// <value>Offset for this list view or 0 for a underlying list.</value> + [Tested] + public virtual int Offset + { + [Tested] + get + { + validitycheck(); +#if HASHINDEX + if (offset == null && underlying != null) + { + //TODO: search from both ends simultaneously! + Node n = underlying.startsentinel; + int i = 0; + while (n != startsentinel) { n = n.next; i++; } + offset = i; + } +#endif + return (int)offset; + } + } + + /// <summary> + /// Slide this list view along the underlying list. + /// </summary> + /// <exception cref="NotAViewException"> if this list is not a view.</exception> + /// <exception cref="ArgumentOutOfRangeException"> if the operation + /// would bring either end of the view outside the underlying list.</exception> + /// <param name="offset">The signed amount to slide: positive to slide + /// towards the end.</param> + [Tested] + public IList<T> Slide(int offset) + { + if (!TrySlide(offset, size)) + throw new ArgumentOutOfRangeException(); + return this; + } + + //TODO: more test cases + /// <summary> + /// Slide this list view along the underlying list, perhaps changing its size. + /// </summary> + /// <exception cref="NotAViewException"> if this list is not a view.</exception> + /// <exception cref="ArgumentOutOfRangeException"> if the operation + /// would bring either end of the view outside the underlying list.</exception> + /// <param name="offset">The signed amount to slide: positive to slide + /// towards the end.</param> + /// <param name="size">The new size of the view.</param> + public IList<T> Slide(int offset, int size) + { + if (!TrySlide(offset, size)) + throw new ArgumentOutOfRangeException(); + return this; + } + + /// <summary> + /// + /// </summary> + /// <param name="offset"></param> + /// <returns></returns> + public virtual bool TrySlide(int offset) { return TrySlide(offset, size); } + + /// <summary> + /// + /// </summary> + /// <param name="offset"></param> + /// <param name="size"></param> + /// <returns></returns> + public virtual bool TrySlide(int offset, int size) + { + updatecheck(); + if (underlying == null) + throw new NotAViewException("List not a view"); + +#pragma warning disable 472 + if (this.offset == null) //Note: only possible with HASHINDEX +#pragma warning restore 472 + { +#pragma warning disable 162 + try + { + getPair(offset - 1, offset + size, out startsentinel, out endsentinel, + new int[] { -1, this.size }, new Node[] { startsentinel, endsentinel }); + //TODO: maybe-update offset field + } + catch (NullReferenceException) + { + return false; + } +#pragma warning restore 162 + } + else + { + if (offset + this.offset < 0 || offset + this.offset + size > underlying.size) + return false; + int oldoffset = (int)(this.offset); + getPair(offset - 1, offset + size, out startsentinel, out endsentinel, + new int[] { -oldoffset - 1, -1, this.size, underlying.size - oldoffset }, + new Node[] { underlying.startsentinel, startsentinel, endsentinel, underlying.endsentinel }); + } + this.size = size; + this.offset += offset; + return true; + } + + + //TODO: improve the complexity of the implementation + /// <summary> + /// + /// <para>Returns null if <code>otherView</code> is strictly to the left of this view</para> + /// </summary> + /// <param name="otherView"></param> + /// <exception cref="IncompatibleViewException">If otherView does not have the same underlying list as this</exception> + /// <returns></returns> + public virtual IList<T> Span(IList<T> otherView) + { + if ((otherView == null) || ((otherView.Underlying ?? otherView) != (underlying ?? this))) + throw new IncompatibleViewException(); + if (otherView.Offset + otherView.Count - Offset < 0) + return null; + return (underlying ?? this).View(Offset, otherView.Offset + otherView.Count - Offset); + } + + + //Question: should we swap items or move nodes around? + //The first seems much more efficient unless the items are value types + //with a large memory footprint. + //(Swapping will do count*3/2 T assignments, linking around will do + // 4*count ref assignments; note that ref assignments are more expensive + //than copying non-ref bits) + /// <summary> + /// Reverse the list so the items are in the opposite sequence order. + /// </summary> + [Tested] + public virtual void Reverse() + { + updatecheck(); + if (size == 0) + return; + + Position[] positions = null; + int poslow = 0, poshigh = 0; + if (views != null) + { + CircularQueue<Position> _positions = null; + foreach (LinkedList<T> view in views) + { + if (view != this) + { + switch (viewPosition(view)) + { + case MutualViewPosition.ContainedIn: + (_positions ?? (_positions = new CircularQueue<Position>())).Enqueue(new Position(view, true)); + _positions.Enqueue(new Position(view, false)); + break; + case MutualViewPosition.Overlapping: + view.Dispose(); + break; + case MutualViewPosition.Contains: + case MutualViewPosition.NonOverlapping: + break; + } + } + } + if (_positions != null) + { + positions = _positions.ToArray(); + Sorting.IntroSort<Position>(positions, 0, positions.Length, PositionComparer.Default); + poshigh = positions.Length - 1; + } + } + + Node a = get(0), b = get(size - 1); + for (int i = 0; i < size / 2; i++) + { + T swap; + swap = a.item; a.item = b.item; b.item = swap; +#if HASHINDEX + dict[a.item] = a; dict[b.item] = b; +#endif + if (positions != null) + mirrorViewSentinelsForReverse(positions, ref poslow, ref poshigh, a, b, i); + a = a.next; b = b.prev; + } + if (positions != null && size % 2 != 0) + mirrorViewSentinelsForReverse(positions, ref poslow, ref poshigh, a, b, size / 2); + (underlying ?? this).raiseCollectionChanged(); + } + + private void mirrorViewSentinelsForReverse(Position[] positions, ref int poslow, ref int poshigh, Node a, Node b, int i) + { +#if HASHINDEX + int? aindex = offset + i, bindex = offset + size - 1 - i; +#else + int aindex = offset + i, bindex = offset + size - 1 - i; +#endif + Position pos; +#if HASHINDEX + while (poslow <= poshigh && (pos = positions[poslow]).Endpoint == a) +#else + while (poslow <= poshigh && (pos = positions[poslow]).Index == aindex) +#endif + { + //TODO: Note: in the case og hashed linked list, if this.offset == null, but pos.View.offset!=null + //we may at this point compute this.offset and non-null values of aindex and bindex + if (pos.Left) + pos.View.endsentinel = b.next; + else + { + pos.View.startsentinel = b.prev; + pos.View.offset = bindex; + } + poslow++; + } +#if HASHINDEX + while (poslow < poshigh && (pos = positions[poshigh]).Endpoint == b) +#else + while (poslow < poshigh && (pos = positions[poshigh]).Index == bindex) +#endif + { + if (pos.Left) + pos.View.endsentinel = a.next; + else + { + pos.View.startsentinel = a.prev; + pos.View.offset = aindex; + } + poshigh--; + } + } + + /// <summary> + /// Check if this list is sorted according to the default sorting order + /// for the item type T, as defined by the <see cref="T:C5.Comparer`1"/> class + /// </summary> + /// <exception cref="NotComparableException">if T is not comparable</exception> + /// <returns>True if the list is sorted, else false.</returns> + public bool IsSorted() { return IsSorted(Comparer<T>.Default); } + + /// <summary> + /// Check if this list is sorted according to a specific sorting order. + /// </summary> + /// <param name="c">The comparer defining the sorting order.</param> + /// <returns>True if the list is sorted, else false.</returns> + [Tested] + public virtual bool IsSorted(SCG.IComparer<T> c) + { + validitycheck(); + if (size <= 1) + return true; + + Node node = startsentinel.next; + T prevItem = node.item; + + node = node.next; + while (node != endsentinel) + { + if (c.Compare(prevItem, node.item) > 0) + return false; + else + { + prevItem = node.item; + node = node.next; + } + } + + return true; + } + + /// <summary> + /// Sort the items of the list according to the default sorting order + /// for the item type T, as defined by the Comparer[T] class. + /// (<see cref="T:C5.Comparer`1"/>). + /// The sorting is stable. + /// </summary> + /// <exception cref="InvalidOperationException">if T is not comparable</exception> + public virtual void Sort() { Sort(Comparer<T>.Default); } + + // Sort the linked list using mergesort + /// <summary> + /// Sort the items of the list according to a specific sorting order. + /// The sorting is stable. + /// </summary> + /// <param name="c">The comparer defining the sorting order.</param> + [Tested] + public virtual void Sort(SCG.IComparer<T> c) + { + updatecheck(); + if (size == 0) + return; + disposeOverlappingViews(false); +#if HASHINDEX + if (underlying != null) + { + Node cursor = startsentinel.next; + while (cursor != endsentinel) + { + cursor.taggroup.count--; + cursor = cursor.next; + } + } +#endif + // Build a linked list of non-empty runs. + // The prev field in first node of a run points to next run's first node + Node runTail = startsentinel.next; + Node prevNode = startsentinel.next; + + endsentinel.prev.next = null; + while (prevNode != null) + { + Node node = prevNode.next; + + while (node != null && c.Compare(prevNode.item, node.item) <= 0) + { + prevNode = node; + node = prevNode.next; + } + + // Completed a run; prevNode is the last node of that run + prevNode.next = null; // Finish the run + runTail.prev = node; // Link it into the chain of runs + runTail = node; + if (c.Compare(endsentinel.prev.item, prevNode.item) <= 0) + endsentinel.prev = prevNode; // Update last pointer to point to largest + + prevNode = node; // Start a new run + } + + // Repeatedly merge runs two and two, until only one run remains + while (startsentinel.next.prev != null) + { + Node run = startsentinel.next; + Node newRunTail = null; + + while (run != null && run.prev != null) + { // At least two runs, merge + Node nextRun = run.prev.prev; + Node newrun = mergeRuns(run, run.prev, c); + + if (newRunTail != null) + newRunTail.prev = newrun; + else + startsentinel.next = newrun; + + newRunTail = newrun; + run = nextRun; + } + + if (run != null) // Add the last run, if any + newRunTail.prev = run; + } + + endsentinel.prev.next = endsentinel; + startsentinel.next.prev = startsentinel; + + //assert invariant(); + //assert isSorted(); +#if HASHINDEX + { + Node cursor = startsentinel.next, end = endsentinel; + int tag, taglimit; + TagGroup t = gettaggroup(startsentinel, endsentinel, out tag, out taglimit); + int tagdelta = taglimit / (size + 1) - tag / (size + 1); + tagdelta = tagdelta == 0 ? 1 : tagdelta; + if (underlying == null) + taggroups = 1; + while (cursor != end) + { + tag = tag + tagdelta > taglimit ? taglimit : tag + tagdelta; + cursor.tag = tag; + t.count++; + cursor.taggroup = t; + cursor = cursor.next; + } + if (t != startsentinel.taggroup) + t.first = startsentinel.next; + if (t != endsentinel.taggroup) + t.last = endsentinel.prev; + if (tag == taglimit) + splittaggroup(t); + } +#endif + (underlying ?? this).raiseCollectionChanged(); + } + + private static Node mergeRuns(Node run1, Node run2, SCG.IComparer<T> c) + { + //assert run1 != null && run2 != null; + Node prev; + bool prev1; // is prev from run1? + + if (c.Compare(run1.item, run2.item) <= 0) + { + prev = run1; + prev1 = true; + run1 = run1.next; + } + else + { + prev = run2; + prev1 = false; + run2 = run2.next; + } + + Node start = prev; + + //assert start != null; + start.prev = null; + while (run1 != null && run2 != null) + { + if (prev1) + { + //assert prev.next == run1; + //Comparable run2item = (Comparable)run2.item; + while (run1 != null && c.Compare(run2.item, run1.item) >= 0) + { + prev = run1; + run1 = prev.next; + } + + if (run1 != null) + { // prev.item <= run2.item < run1.item; insert run2 + prev.next = run2; + run2.prev = prev; + prev = run2; + run2 = prev.next; + prev1 = false; + } + } + else + { + //assert prev.next == run2; + //Comparable run1item = (Comparable)run1.item; + while (run2 != null && c.Compare(run1.item, run2.item) > 0) + { + prev = run2; + run2 = prev.next; + } + + if (run2 != null) + { // prev.item < run1.item <= run2.item; insert run1 + prev.next = run1; + run1.prev = prev; + prev = run1; + run1 = prev.next; + prev1 = true; + } + } + } + + //assert !(run1 != null && prev1) && !(run2 != null && !prev1); + if (run1 != null) + { // last run2 < all of run1; attach run1 at end + prev.next = run1; + run1.prev = prev; + } + else if (run2 != null) + { // last run1 + prev.next = run2; + run2.prev = prev; + } + + return start; + } + + /// <summary> + /// Randomly shuffle the items of this list. + /// <para>Will invalidate overlapping views???</para> + /// </summary> + public virtual void Shuffle() { Shuffle(new C5Random()); } + + + /// <summary> + /// Shuffle the items of this list according to a specific random source. + /// <para>Will invalidate overlapping views???</para> + /// </summary> + /// <param name="rnd">The random source.</param> + public virtual void Shuffle(Random rnd) + { + updatecheck(); + if (size == 0) + return; + disposeOverlappingViews(false); + ArrayList<T> a = new ArrayList<T>(); + a.AddAll(this); + a.Shuffle(rnd); + Node cursor = startsentinel.next; + int j = 0; + while (cursor != endsentinel) + { + cursor.item = a[j++]; +#if HASHINDEX + dict[cursor.item] = cursor; +#endif + cursor = cursor.next; + } + (underlying ?? this).raiseCollectionChanged(); + } + + #endregion + + #region IIndexed<T> Members + + /// <summary> + /// <exception cref="IndexOutOfRangeException"/>. + /// </summary> + /// <value>The directed collection of items in a specific index interval.</value> + /// <param name="start">The low index of the interval (inclusive).</param> + /// <param name="count">The size of the range.</param> + [Tested] + public IDirectedCollectionValue<T> this[int start, int count] + { + [Tested] + get + { + validitycheck(); + checkRange(start, count); + return new Range(this, start, count, true); + } + } + + /// <summary> + /// Searches for an item in the list going forwrds from the start. + /// </summary> + /// <param name="item">Item to search for.</param> + /// <returns>Index of item from start.</returns> + [Tested] + public virtual int IndexOf(T item) + { + validitycheck(); + Node node; +#if HASHINDEX + if (!dict.Find(item, out node) || !insideview(node)) + return ~size; +#endif + node = startsentinel.next; + int index = 0; + if (find(item, ref node, ref index)) + return index; + else + return ~size; + } + + /// <summary> + /// Searches for an item in the list going backwords from the end. + /// </summary> + /// <param name="item">Item to search for.</param> + /// <returns>Index of of item from the end.</returns> + [Tested] + public virtual int LastIndexOf(T item) + { +#if HASHINDEX + return IndexOf(item); +#else + validitycheck(); + + Node node = endsentinel.prev; + int index = size - 1; + + if (dnif(item, ref node, ref index)) + return index; + else + return ~size; +#endif + } + + /// <summary> + /// Remove the item at a specific position of the list. + /// <exception cref="IndexOutOfRangeException"/> if i is negative or + /// >= the size of the collection. + /// </summary> + /// <param name="i">The index of the item to remove.</param> + /// <returns>The removed item.</returns> + [Tested] + public virtual T RemoveAt(int i) + { + updatecheck(); + T retval = remove(get(i), i); +#if HASHINDEX + dict.Remove(retval); +#endif + if (ActiveEvents != EventTypeEnum.None) + (underlying ?? this).raiseForRemoveAt(Offset + i, retval); + return retval; + } + + /// <summary> + /// Remove all items in an index interval. + /// <exception cref="IndexOutOfRangeException"/>???. + /// </summary> + /// <param name="start">The index of the first item to remove.</param> + /// <param name="count">The number of items to remove.</param> + [Tested] + public virtual void RemoveInterval(int start, int count) + { +#if HASHINDEX + updatecheck(); + checkRange(start, count); + if (count == 0) + return; + + View(start, count).Clear(); +#else + //Note: this is really almost equaivalent to Clear on a view + updatecheck(); + checkRange(start, count); + if (count == 0) + return; + + //for small count: optimize + //use an optimal get(int i, int j, ref Node ni, ref Node nj)? + Node a = get(start), b = get(start + count - 1); + fixViewsBeforeRemove(start, count, a, b); + a.prev.next = b.next; + b.next.prev = a.prev; + if (underlying != null) + underlying.size -= count; + + size -= count; + if (ActiveEvents != EventTypeEnum.None) + (underlying ?? this).raiseForRemoveInterval(start + Offset, count); +#endif + } + + void raiseForRemoveInterval(int start, int count) + { + if (ActiveEvents != 0) + { + raiseCollectionCleared(size == 0, count, start); + raiseCollectionChanged(); + } + } + #endregion + + #region ISequenced<T> Members + + /// <summary> + /// + /// </summary> + /// <returns></returns> + [Tested] + public override int GetSequencedHashCode() { validitycheck(); return base.GetSequencedHashCode(); } + + /// <summary> + /// + /// </summary> + /// <param name="that"></param> + /// <returns></returns> + [Tested] + public override bool SequencedEquals(ISequenced<T> that) { validitycheck(); return base.SequencedEquals(that); } + + #endregion + + #region IDirectedCollection<T> Members + + /// <summary> + /// Create a collection containing the same items as this collection, but + /// whose enumerator will enumerate the items backwards. The new collection + /// will become invalid if the original is modified. Method typicaly used as in + /// <code>foreach (T x in coll.Backwards()) {...}</code> + /// </summary> + /// <returns>The backwards collection.</returns> + [Tested] + public override IDirectedCollectionValue<T> Backwards() + { return this[0, size].Backwards(); } + + #endregion + + #region IDirectedEnumerable<T> Members + + [Tested] + IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return Backwards(); } + + #endregion + + #region IEditableCollection<T> Members + + /// <summary> + /// The value is symbolic indicating the type of asymptotic complexity + /// in terms of the size of this collection (worst-case or amortized as + /// relevant). + /// </summary> + /// <value>Speed.Linear</value> + [Tested] + public virtual Speed ContainsSpeed + { + [Tested] + get + { +#if HASHINDEX + return Speed.Constant; +#else + return Speed.Linear; +#endif + } + } + + /// <summary> + /// Performs a check for view validity before calling base.GetUnsequencedHashCode() + /// </summary> + /// <returns></returns> + [Tested] + public override int GetUnsequencedHashCode() + { validitycheck(); return base.GetUnsequencedHashCode(); } + + /// <summary> + /// + /// </summary> + /// <param name="that"></param> + /// <returns></returns> + [Tested] + public override bool UnsequencedEquals(ICollection<T> that) + { validitycheck(); return base.UnsequencedEquals(that); } + + /// <summary> + /// Check if this collection contains (an item equivalent to according to the + /// itemequalityComparer) a particular value. + /// </summary> + /// <param name="item">The value to check for.</param> + /// <returns>True if the items is in this collection.</returns> + [Tested] + public virtual bool Contains(T item) + { + validitycheck(); + Node node; + return contains(item, out node); + } + + /// <summary> + /// Check if this collection contains an item equivalent according to the + /// itemequalityComparer to a particular value. If so, return in the ref argument (a + /// binary copy of) the actual value found. + /// </summary> + /// <param name="item">The value to look for.</param> + /// <returns>True if the items is in this collection.</returns> + [Tested] + public virtual bool Find(ref T item) + { + validitycheck(); + Node node; + if (contains(item, out node)) { item = node.item; return true; } + return false; + } + + /// <summary> + /// Check if this collection contains an item equivalent according to the + /// itemequalityComparer to a particular value. If so, update the item in the collection + /// to with a binary copy of the supplied value. Will update a single item. + /// </summary> + /// <param name="item">Value to update.</param> + /// <returns>True if the item was found and hence updated.</returns> + [Tested] + public virtual bool Update(T item) { T olditem; return Update(item, out olditem); } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <param name="olditem"></param> + /// <returns></returns> + public virtual bool Update(T item, out T olditem) + { + updatecheck(); + Node node; + + if (contains(item, out node)) + { + olditem = node.item; + node.item = item; +#if HASHINDEX + //Avoid clinging onto a reference to olditem via dict! + dict.Update(item, node); +#endif + (underlying ?? this).raiseForUpdate(item, olditem); + return true; + } + + olditem = default(T); + return false; + } + + /// <summary> + /// Check if this collection contains an item equivalent according to the + /// itemequalityComparer to a particular value. If so, return in the ref argument (a + /// binary copy of) the actual value found. Else, add the item to the collection. + /// </summary> + /// <param name="item">The value to look for.</param> + /// <returns>True if the item was found (hence not added).</returns> + [Tested] + public virtual bool FindOrAdd(ref T item) + { + updatecheck(); +#if HASHINDEX + //This is an extended myinsert: + Node node = new Node(item); + if (!dict.FindOrAdd(item, ref node)) + { + insertNode(true, endsentinel, node); + (underlying ?? this).raiseForAdd(item); + return false; + } + if (!insideview(node)) + throw new ArgumentException("Item alredy in indexed list but outside view"); + item = node.item; + return true; +#else + if (Find(ref item)) + return true; + + Add(item); + return false; +#endif + } + + /// <summary> + /// Check if this collection contains an item equivalent according to the + /// itemequalityComparer to a particular value. If so, update the item in the collection + /// to with a binary copy of the supplied value; else add the value to the collection. + /// </summary> + /// <param name="item">Value to add or update.</param> + /// <returns>True if the item was found and updated (hence not added).</returns> + [Tested] + public virtual bool UpdateOrAdd(T item) { T olditem; return UpdateOrAdd(item, out olditem); } + + /// <summary> + /// + /// </summary> + /// <param name="item"></param> + /// <param name="olditem"></param> + /// <returns></returns> + public virtual bool UpdateOrAdd(T item, out T olditem) + { + updatecheck(); +#if HASHINDEX + Node node = new Node(item); + //NOTE: it is hard to do this without double access to the dictionary + //in the update case + if (dict.FindOrAdd(item, ref node)) + { + if (!insideview(node)) + throw new ArgumentException("Item in indexed list but outside view"); + olditem = node.item; + //Avoid clinging onto a reference to olditem via dict! + dict.Update(item, node); + node.item = item; + (underlying ?? this).raiseForUpdate(item, olditem); + return true; + } + insertNode(true, endsentinel, node); + (underlying ?? this).raiseForAdd(item); +#else + if (Update(item, out olditem)) + return true; + Add(item); +#endif + olditem = default(T); + return false; + } + + /// <summary> + /// Remove a particular item from this collection. Since the collection has bag + /// semantics only one copy equivalent to the supplied item is removed. + /// </summary> + /// <param name="item">The value to remove.</param> + /// <returns>True if the item was found (and removed).</returns> + [Tested] + public virtual bool Remove(T item) + { + updatecheck(); + int i = 0; + Node node; +#if HASHINDEX + if (!dictremove(item, out node)) +#else + node = fIFO ? startsentinel.next : endsentinel.prev; + if (!(fIFO ? find(item, ref node, ref i) : dnif(item, ref node, ref i))) +#endif + return false; + T removeditem = remove(node, i); + (underlying ?? this).raiseForRemove(removeditem); + return true; + } + + /// <summary> + /// Remove a particular item from this collection if found (only one copy). + /// If an item was removed, report a binary copy of the actual item removed in + /// the argument. + /// </summary> + /// <param name="item">The value to remove on input.</param> + /// <param name="removeditem">The value removed.</param> + /// <returns>True if the item was found (and removed).</returns> + [Tested] + public virtual bool Remove(T item, out T removeditem) + { + updatecheck(); + int i = 0; + Node node; +#if HASHINDEX + if (!dictremove(item, out node)) +#else + node = fIFO ? startsentinel.next : endsentinel.prev; + if (!(fIFO ? find(item, ref node, ref i) : dnif(item, ref node, ref i))) +#endif + { + removeditem = default(T); + return false; + } + removeditem = node.item; + remove(node, i); + (underlying ?? this).raiseForRemove(removeditem); + return true; + } + + /// <summary> + /// Remove all items in another collection from this one, taking multiplicities into account. + /// <para>Always removes from the front of the list. + /// </para> + /// <para>The asymptotic running time complexity of this method is <code>O(n+m+v*log(v))</code>, + /// where <code>n</code> is the size of this list, <code>m</code> is the size of the + /// <code>items</code> collection and <code>v</code> is the number of views. + /// The method will temporarily allocate memory of size <code>O(m+v)</code>. + /// </para> + /// </summary> + /// <typeparam name="U"></typeparam> + /// <param name="items">The items to remove.</param> + [Tested] + public virtual void RemoveAll<U>(SCG.IEnumerable<U> items) where U : T + { + updatecheck(); + if (size == 0) + return; + RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this); + bool mustFire = raiseHandler.MustFire; +#if HASHINDEX + Node node; + foreach (T item in items) + if (dictremove(item, out node)) + { + if (mustFire) + raiseHandler.Remove(node.item); + remove(node, 118); + } +#else + HashBag<T> toremove = new HashBag<T>(itemequalityComparer); + toremove.AddAll(items); + ViewHandler viewHandler = new ViewHandler(this); + int index = 0, removed = 0, myoffset = Offset; + Node node = startsentinel.next; + while (node != endsentinel) + { + //pass by a stretch of nodes + while (node != endsentinel && !toremove.Contains(node.item)) + { + node = node.next; + index++; + } + viewHandler.skipEndpoints(removed, myoffset + index); + //Remove a stretch of nodes + Node localend = node.prev; //Latest node not to be removed + while (node != endsentinel && toremove.Remove(node.item)) + { + if (mustFire) + raiseHandler.Remove(node.item); + removed++; + node = node.next; + index++; + viewHandler.updateViewSizesAndCounts(removed, myoffset + index); + } + viewHandler.updateSentinels(myoffset + index, localend, node); + localend.next = node; + node.prev = localend; + } + index = underlying != null ? underlying.size + 1 - myoffset : size + 1 - myoffset; + viewHandler.updateViewSizesAndCounts(removed, myoffset + index); + size -= removed; + if (underlying != null) + underlying.size -= removed; +#endif + raiseHandler.Raise(); + } + + /// <summary> + /// + /// </summary> + /// <param name="predicate"></param> + void RemoveAll(Fun<T, bool> predicate) + { + updatecheck(); + if (size == 0) + return; + RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this); + bool mustFire = raiseHandler.MustFire; +#if HASHINDEX + { + Node n = startsentinel.next; + + while (n != endsentinel) + { + bool removeIt = predicate(n.item); + updatecheck(); + if (removeIt) + { + dict.Remove(n.item); + remove(n, 119); + if (mustFire) + raiseHandler.Remove(n.item); + } + + n = n.next; + } + } +#else + ViewHandler viewHandler = new ViewHandler(this); + int index = 0, removed = 0, myoffset = Offset; + Node node = startsentinel.next; + while (node != endsentinel) + { + //pass by a stretch of nodes + while (node != endsentinel && !predicate(node.item)) + { + updatecheck(); + node = node.next; + index++; + } + updatecheck(); + viewHandler.skipEndpoints(removed, myoffset + index); + //Remove a stretch of nodes + Node localend = node.prev; //Latest node not to be removed + while (node != endsentinel && predicate(node.item)) + { + updatecheck(); + if (mustFire) + raiseHandler.Remove(node.item); + removed++; + node = node.next; + index++; + viewHandler.updateViewSizesAndCounts(removed, myoffset + index); + } + updatecheck(); + viewHandler.updateSentinels(myoffset + index, localend, node); + localend.next = node; + node.prev = localend; + } + index = underlying != null ? underlying.size + 1 - myoffset : size + 1 - myoffset; + viewHandler.updateViewSizesAndCounts(removed, myoffset + index); + size -= removed; + if (underlying != null) + underlying.size -= removed; +#endif + raiseHandler.Raise(); + } + + /// <summary> + /// Remove all items from this collection. + /// </summary> + [Tested] + public virtual void Clear() + { + updatecheck(); + if (size == 0) + return; + int oldsize = size; +#if HASHINDEX + if (underlying == null) + dict.Clear(); + else + foreach (T item in this) + dict.Remove(item); +#endif + clear(); + (underlying ?? this).raiseForRemoveInterval(Offset, oldsize); + } + + void clear() + { + if (size == 0) + return; +#if HASHINDEX + //TODO: mix with tag maintenance to only run through list once? + ViewHandler viewHandler = new ViewHandler(this); + if (viewHandler.viewCount > 0) + { + int removed = 0; + Node n = startsentinel.next; + viewHandler.skipEndpoints(0, n); + while (n != endsentinel) + { + removed++; + n = n.next; + viewHandler.updateViewSizesAndCounts(removed, n); + } + viewHandler.updateSentinels(endsentinel, startsentinel, endsentinel); + if (underlying != null) + viewHandler.updateViewSizesAndCounts(removed, underlying.endsentinel); + } +#else + fixViewsBeforeRemove(Offset, size, startsentinel.next, endsentinel.prev); +#endif +#if HASHINDEX + if (underlying != null) + { + Node n = startsentinel.next; + + while (n != endsentinel) + { + n.next.prev = startsentinel; + startsentinel.next = n.next; + removefromtaggroup(n); + n = n.next; + } + } + else + taggroups = 0; +#endif + endsentinel.prev = startsentinel; + startsentinel.next = endsentinel; + if (underlying != null) + underlying.size -= size; + size = 0; + } + + /// <summary> + /// Remove all items not in some other collection from this one, taking multiplicities into account. + /// <para>The asymptotic running time complexity of this method is <code>O(n+m+v*log(v))</code>, + /// where <code>n</code> is the size of this collection, <code>m</code> is the size of the + /// <code>items</code> collection and <code>v</code> is the number of views. + /// The method will temporarily allocate memory of size <code>O(m+v)</code>. The stated complexitiy + /// holds under the assumption that the itemequalityComparer of this list is well-behaved. + /// </para> + /// </summary> + /// <typeparam name="U"></typeparam> + /// <param name="items">The items to retain.</param> + [Tested] + public virtual void RetainAll<U>(SCG.IEnumerable<U> items) where U : T + { + updatecheck(); + if (size == 0) + return; + RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this); + bool mustFire = raiseHandler.MustFire; +#if HASHINDEX + /*if (underlying == null) + { + HashDictionary<T, Node> newdict = new HashDictionary<T, Node>(itemequalityComparer); + foreach (T item in items) + { + Node node; + + if (dict.Remove(item, out node)) + newdict.Add(item, node); + } + foreach (KeyValuePair<T, Node> pair in dict) + { + Node n = pair.Value; + fixViewsBeforeSingleRemove(n, 117); + Node p = n.prev, s = n.next; s.prev = p; p.next = s; + removefromtaggroup(n); + } + dict = newdict; + size = dict.Count; + //For a small number of items to retain it might be faster to + //iterate through the list and splice out the chunks not needed + } + else*/ + { + HashSet<T> toremove = new HashSet<T>(itemequalityComparer); + + foreach (T item in this) + toremove.Add(item); + + foreach (T item in items) + toremove.Remove(item); + + Node n = startsentinel.next; + + while (n != endsentinel && toremove.Count > 0) + { + if (toremove.Contains(n.item)) + { + dict.Remove(n.item); + remove(n, 119); + if (mustFire) + raiseHandler.Remove(n.item); + } + + n = n.next; + } + } +#else + HashBag<T> toretain = new HashBag<T>(itemequalityComparer); + toretain.AddAll(items); + ViewHandler viewHandler = new ViewHandler(this); + int index = 0, removed = 0, myoffset = Offset; + Node node = startsentinel.next; + while (node != endsentinel) + { + //Skip a stretch of nodes + while (node != endsentinel && toretain.Remove(node.item)) + { + node = node.next; + index++; + } + viewHandler.skipEndpoints(removed, myoffset + index); + //Remove a stretch of nodes + Node localend = node.prev; //Latest node not to be removed + while (node != endsentinel && !toretain.Contains(node.item)) + { + if (mustFire) + raiseHandler.Remove(node.item); + removed++; + node = node.next; + index++; + viewHandler.updateViewSizesAndCounts(removed, myoffset + index); + } + viewHandler.updateSentinels(myoffset + index, localend, node); + localend.next = node; + node.prev = localend; + } + index = underlying != null ? underlying.size + 1 - myoffset : size + 1 - myoffset; + viewHandler.updateViewSizesAndCounts(removed, myoffset + index); + size -= removed; + if (underlying != null) + underlying.size -= removed; +#endif + raiseHandler.Raise(); + } + + /// <summary> + /// + /// </summary> + /// <param name="predicate"></param> + void RetainAll(Fun<T, bool> predicate) + { + updatecheck(); + if (size == 0) + return; + RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this); + bool mustFire = raiseHandler.MustFire; +#if HASHINDEX + { + Node n = startsentinel.next; + + while (n != endsentinel) + { + bool removeIt = !predicate(n.item); + updatecheck(); + if (removeIt) + { + dict.Remove(n.item); + remove(n, 119); + if (mustFire) + raiseHandler.Remove(n.item); + } + + n = n.next; + } + } +#else + ViewHandler viewHandler = new ViewHandler(this); + int index = 0, removed = 0, myoffset = Offset; + Node node = startsentinel.next; + while (node != endsentinel) + { + //Skip a stretch of nodes + while (node != endsentinel && predicate(node.item)) + { + updatecheck(); + node = node.next; + index++; + } + updatecheck(); + viewHandler.skipEndpoints(removed, myoffset + index); + //Remove a stretch of nodes + Node localend = node.prev; //Latest node not to be removed + while (node != endsentinel && !predicate(node.item)) + { + updatecheck(); + if (mustFire) + raiseHandler.Remove(node.item); + removed++; + node = node.next; + index++; + viewHandler.updateViewSizesAndCounts(removed, myoffset + index); + } + updatecheck(); + viewHandler.updateSentinels(myoffset + index, localend, node); + localend.next = node; + node.prev = localend; + } + index = underlying != null ? underlying.size + 1 - myoffset : size + 1 - myoffset; + viewHandler.updateViewSizesAndCounts(removed, myoffset + index); + size -= removed; + if (underlying != null) + underlying.size -= removed; +#endif + raiseHandler.Raise(); + } + + /// <summary> + /// Check if this collection contains all the values in another collection + /// with respect to multiplicities. + /// </summary> + /// <param name="items">The </param> + /// <typeparam name="U"></typeparam> + /// <returns>True if all values in <code>items</code>is in this collection.</returns> + [Tested] + public virtual bool ContainsAll<U>(SCG.IEnumerable<U> items) where U : T + { + validitycheck(); +#if HASHINDEX + Node node; + foreach (T item in items) + if (!contains(item, out node)) + return false; + return true; +#else + HashBag<T> tocheck = new HashBag<T>(itemequalityComparer); + tocheck.AddAll(items); + if (tocheck.Count > size) + return false; + Node node = startsentinel.next; + while (node != endsentinel) + { + tocheck.Remove(node.item); + node = node.next; + } + return tocheck.IsEmpty; +#endif + } + + + /// <summary> + /// Create a new list consisting of the items of this list satisfying a + /// certain predicate. + /// </summary> + /// <param name="filter">The filter delegate defining the predicate.</param> + /// <returns>The new list.</returns> + [Tested] + public IList<T> FindAll(Fun<T, bool> filter) + { + validitycheck(); + int stamp = this.stamp; + LinkedList<T> retval = new LinkedList<T>(); + Node cursor = startsentinel.next; + Node mcursor = retval.startsentinel; +#if HASHINDEX + double tagdelta = int.MaxValue / (size + 1.0); + int count = 1; + TagGroup taggroup = new TagGroup(); + retval.taggroups = 1; +#endif + while (cursor != endsentinel) + { + bool found = filter(cursor.item); + modifycheck(stamp); + if (found) + { + mcursor.next = new Node(cursor.item, mcursor, null); + mcursor = mcursor.next; + retval.size++; +#if HASHINDEX + retval.dict.Add(cursor.item, mcursor); + mcursor.taggroup = taggroup; + mcursor.tag = (int)(tagdelta * count++); +#endif + } + cursor = cursor.next; + } +#if HASHINDEX + if (retval.size > 0) + { + taggroup.count = retval.size; + taggroup.first = retval.startsentinel.next; + taggroup.last = mcursor; + } +#endif + retval.endsentinel.prev = mcursor; + mcursor.next = retval.endsentinel; + return retval; + } + + + /// <summary> + /// Count the number of items of the collection equal to a particular value. + /// Returns 0 if and only if the value is not in the collection. + /// </summary> + /// <param name="item">The value to count.</param> + /// <returns>The number of copies found.</returns> + [Tested] + public virtual int ContainsCount(T item) + { +#if HASHINDEX + return Contains(item) ? 1 : 0; +#else + validitycheck(); + int retval = 0; + Node node = startsentinel.next; + while (node != endsentinel) + { + if (itemequalityComparer.Equals(node.item, item)) + retval++; + node = node.next; + } + return retval; +#endif + } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public virtual ICollectionValue<T> UniqueItems() + { +#if HASHINDEX + return this; +#else + HashBag<T> hashbag = new HashBag<T>(itemequalityComparer); + hashbag.AddAll(this); + return hashbag.UniqueItems(); +#endif + } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public virtual ICollectionValue<KeyValuePair<T, int>> ItemMultiplicities() + { +#if HASHINDEX + return new MultiplicityOne<T>(this); +#else + HashBag<T> hashbag = new HashBag<T>(itemequalityComparer); + hashbag.AddAll(this); + return hashbag.ItemMultiplicities(); +#endif + } + + /// <summary> + /// Remove all items equivalent to a given value. + /// <para>The asymptotic complexity of this method is <code>O(n+v*log(v))</code>, + /// where <code>n</code> is the size of the collection and <code>v</code> + /// is the number of views. + /// </para> + /// </summary> + /// <param name="item">The value to remove.</param> + [Tested] + public virtual void RemoveAllCopies(T item) + { +#if HASHINDEX + Remove(item); +#else + updatecheck(); + if (size == 0) + return; + RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this); + bool mustFire = raiseHandler.MustFire; + ViewHandler viewHandler = new ViewHandler(this); + int index = 0, removed = 0, myoffset = Offset; + // + Node node = startsentinel.next; + while (node != endsentinel) + { + //pass by a stretch of nodes + while (node != endsentinel && !itemequalityComparer.Equals(node.item, item)) + { + node = node.next; + index++; + } + viewHandler.skipEndpoints(removed, myoffset + index); + //Remove a stretch of nodes + Node localend = node.prev; //Latest node not to be removed + while (node != endsentinel && itemequalityComparer.Equals(node.item, item)) + { + if (mustFire) + raiseHandler.Remove(node.item); + removed++; + node = node.next; + index++; + viewHandler.updateViewSizesAndCounts(removed, myoffset + index); + } + viewHandler.updateSentinels(myoffset + index, localend, node); + localend.next = node; + node.prev = localend; + } + index = underlying != null ? underlying.size + 1 - myoffset : size + 1 - myoffset; + viewHandler.updateViewSizesAndCounts(removed, myoffset + index); + size -= removed; + if (underlying != null) + underlying.size -= removed; + raiseHandler.Raise(); +#endif + } + + #endregion + + #region ICollectionValue<T> Members + + /// <summary> + /// + /// </summary> + /// <value>The number of items in this collection</value> + [Tested] + public override int Count { [Tested]get { validitycheck(); return size; } } + + /// <summary> + /// Choose some item of this collection. + /// </summary> + /// <exception cref="NoSuchItemException">if collection is empty.</exception> + /// <returns></returns> + [Tested] + public override T Choose() { return First; } + + /// <summary> + /// Create an enumerable, enumerating the items of this collection that satisfies + /// a certain condition. + /// </summary> + /// <param name="filter">The T->bool filter delegate defining the condition</param> + /// <returns>The filtered enumerable</returns> + public override SCG.IEnumerable<T> Filter(Fun<T, bool> filter) { validitycheck(); return base.Filter(filter); } + + #endregion + + #region IEnumerable<T> Members + /// <summary> + /// Create an enumerator for the collection + /// </summary> + /// <returns>The enumerator</returns> + [Tested] + public override SCG.IEnumerator<T> GetEnumerator() + { + validitycheck(); + Node cursor = startsentinel.next; + int enumeratorstamp = underlying != null ? underlying.stamp : this.stamp; + + while (cursor != endsentinel) + { + modifycheck(enumeratorstamp); + yield return cursor.item; + cursor = cursor.next; + } + } + + #endregion + + #region IExtensible<T> Members + /// <summary> + /// Add an item to this collection if possible. + /// </summary> + /// <param name="item">The item to add.</param> + /// <returns>True.</returns> + [Tested] + public virtual bool Add(T item) + { + updatecheck(); +#if HASHINDEX + Node node = new Node(item); + if (!dict.FindOrAdd(item, ref node)) + { + insertNode(true, endsentinel, node); + (underlying ?? this).raiseForAdd(item); + return true; + } + return false; +#else + insert(size, endsentinel, item); + (underlying ?? this).raiseForAdd(item); + return true; +#endif + } + + /// <summary> + /// + /// </summary> + /// <value>True since this collection has bag semantics.</value> + [Tested] + public virtual bool AllowsDuplicates + { + [Tested] + get + { +#if HASHINDEX + return false; +#else + return true; +#endif + } + } + + /// <summary> + /// By convention this is true for any collection with set semantics. + /// </summary> + /// <value>True if only one representative of a group of equal items + /// is kept in the collection together with the total count.</value> + public virtual bool DuplicatesByCounting + { + get + { +#if HASHINDEX + return true; +#else + return false; +#endif + } + } + + /// <summary> + /// Add the elements from another collection with a more specialized item type + /// to this collection. + /// </summary> + /// <typeparam name="U">The type of items to add</typeparam> + /// <param name="items">The items to add</param> + [Tested] + public virtual void AddAll<U>(SCG.IEnumerable<U> items) where U : T + { +#if HASHINDEX + updatecheck(); + int added = 0; + Node pred = endsentinel.prev; + foreach (U item in items) + { + Node node = new Node(item); + if (!dict.FindOrAdd(item, ref node)) + { + insertNode(false, endsentinel, node); + added++; + } + } + if (added > 0) + { + fixViewsAfterInsert(endsentinel, pred, added, 0); + raiseForInsertAll(pred, size - added, added, false); + } +#else + insertAll(size, items, false); +#endif + } + + #endregion + +#if HASHINDEX +#else + #region IStack<T> Members + + /// <summary> + /// Push an item to the top of the stack. + /// </summary> + /// <param name="item">The item</param> + [Tested] + public void Push(T item) + { + InsertLast(item); + } + + /// <summary> + /// Pop the item at the top of the stack from the stack. + /// </summary> + /// <returns>The popped item.</returns> + [Tested] + public T Pop() + { + return RemoveLast(); + } + + #endregion + + #region IQueue<T> Members + + /// <summary> + /// Enqueue an item at the back of the queue. + /// </summary> + /// <param name="item">The item</param> + [Tested] + public virtual void Enqueue(T item) + { + InsertLast(item); + } + + /// <summary> + /// Dequeue an item from the front of the queue. + /// </summary> + /// <returns>The item</returns> + [Tested] + public virtual T Dequeue() + { + return RemoveFirst(); + } + #endregion +#endif + + #region Diagnostic + + private bool checkViews() + { + if (underlying != null) + throw new InternalException(System.Reflection.MethodInfo.GetCurrentMethod() + " called on a view"); + if (views == null) + return true; + bool retval = true; + + Node[] nodes = new Node[size + 2]; + int i = 0; + Node n = startsentinel; + while (n != null) + { + nodes[i++] = n; + n = n.next; + } + //Console.WriteLine("###"); + foreach (LinkedList<T> view in views) + { + if (!view.isValid) + { + Console.WriteLine("Invalid view(hash {0}, offset {1}, size {2})", + view.GetHashCode(), view.offset, view.size); + retval = false; + continue; + } + if (view.Offset > size || view.Offset < 0) + { + Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), Offset > underlying.size ({2})", + view.GetHashCode(), view.offset, view.size, size); + retval = false; + } + else if (view.startsentinel != nodes[view.Offset]) + { + Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), startsentinel {3} should be {4}", + view.GetHashCode(), view.offset, view.size, + view.startsentinel + " " + view.startsentinel.GetHashCode(), + nodes[view.Offset] + " " + nodes[view.Offset].GetHashCode()); + retval = false; + } + if (view.Offset + view.size > size || view.Offset + view.size < 0) + { + Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), end index > underlying.size ({3})", + view.GetHashCode(), view.offset, view.size, size); + retval = false; + } + else if (view.endsentinel != nodes[view.Offset + view.size + 1]) + { + Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), endsentinel {3} should be {4}", + view.GetHashCode(), view.offset, view.size, + view.endsentinel + " " + view.endsentinel.GetHashCode(), + nodes[view.Offset + view.size + 1] + " " + nodes[view.Offset + view.size + 1].GetHashCode()); + retval = false; + } + if (view.views != views) + { + Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), wrong views list {3} <> {4}", + view.GetHashCode(), view.offset, view.size, view.views.GetHashCode(), views.GetHashCode()); + retval = false; + } + if (view.underlying != this) + { + Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), wrong underlying {3} <> this {4}", + view.GetHashCode(), view.offset, view.size, view.underlying.GetHashCode(), GetHashCode()); + retval = false; + } + if (view.stamp != stamp) + { + //Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), wrong stamp view:{2} underlying: {3}", view.GetHashCode(),view.offset, view.size, view.stamp, stamp); + //retval = false; + } + } + return retval; + } + + string zeitem(Node node) + { + return node == null ? "(null node)" : node.item.ToString(); + } + + /// <summary> + /// Check the sanity of this list + /// </summary> + /// <returns>true if sane</returns> + [Tested] + public virtual bool Check() + { + bool retval = true; + + /*if (underlying != null && underlying.stamp != stamp) + { + Console.WriteLine("underlying != null && underlying.stamp({0}) != stamp({1})", underlying.stamp, stamp); + retval = false; + }*/ + + if (underlying != null) + { + //TODO: check that this view is included in viewsEndpoints tree + return underlying.Check(); + } + + if (startsentinel == null) + { + Console.WriteLine("startsentinel == null"); + retval = false; + } + + if (endsentinel == null) + { + Console.WriteLine("endsentinel == null"); + retval = false; + } + + if (size == 0) + { + if (startsentinel != null && startsentinel.next != endsentinel) + { + Console.WriteLine("size == 0 but startsentinel.next != endsentinel"); + retval = false; + } + + if (endsentinel != null && endsentinel.prev != startsentinel) + { + Console.WriteLine("size == 0 but endsentinel.prev != startsentinel"); + retval = false; + } + } + + if (startsentinel == null) + { + Console.WriteLine("NULL startsentinel"); + return retval; + } + + int count = 0; + Node node = startsentinel.next, prev = startsentinel; +#if HASHINDEX + int taggroupsize = 0, oldtaggroupsize = losize + 1, seentaggroups = 0; + TagGroup oldtg = null; + + if (underlying == null) + { + TagGroup tg = startsentinel.taggroup; + + if (tg.count != 0 || tg.first != null || tg.last != null || tg.tag != int.MinValue) + { + Console.WriteLine("Bad startsentinel tag group: {0}", tg); + retval = false; + } + + tg = endsentinel.taggroup; + if (tg.count != 0 || tg.first != null || tg.last != null || tg.tag != int.MaxValue) + { + Console.WriteLine("Bad endsentinel tag group: {0}", tg); + retval = false; + } + } +#endif + while (node != endsentinel) + { + count++; + if (node.prev != prev) + { + Console.WriteLine("Bad backpointer at node {0}", count); + retval = false; + } +#if HASHINDEX + if (underlying == null) + { + if (!node.prev.precedes(node)) + { + Console.WriteLine("node.prev.tag ({0}, {1}) >= node.tag ({2}, {3}) at index={4} item={5} ", node.prev.taggroup.tag, node.prev.tag, node.taggroup.tag, node.tag, count, node.item); + retval = false; + } + + if (node.taggroup != oldtg) + { + + if (node.taggroup.first != node) + { + string ntfi = zeitem(node.taggroup.first); + Console.WriteLine("Bad first pointer in taggroup: node.taggroup.first.item ({0}), node.item ({1}) at index={2} item={3}", ntfi, node.item, count, node.item); + retval = false; + } + + if (oldtg != null) + { + if (oldtg.count != taggroupsize) + { + Console.WriteLine("Bad taggroupsize: oldtg.count ({0}) != taggroupsize ({1}) at index={2} item={3}", oldtg.count, taggroupsize, count, node.item); + retval = false; + } + + if (oldtaggroupsize <= losize && taggroupsize <= losize) + { + Console.WriteLine("Two small taggroups in a row: oldtaggroupsize ({0}), taggroupsize ({1}) at index={2} item={3}", oldtaggroupsize, taggroupsize, count, node.item); + retval = false; + } + + if (node.taggroup.tag <= oldtg.tag) + { + Console.WriteLine("Taggroup tags not strictly increasing: oldtaggrouptag ({0}), taggrouptag ({1}) at index={2} item={3}", oldtg.tag, node.taggroup.tag, count, node.item); + retval = false; + } + + if (oldtg.last != node.prev) + { + Console.WriteLine("Bad last pointer in taggroup: oldtg.last.item ({0}), node.prev.item ({1}) at index={2} item={3}", oldtg.last.item, node.prev.item, count, node.item); + retval = false; + } + + oldtaggroupsize = taggroupsize; + } + + seentaggroups++; + oldtg = node.taggroup; + taggroupsize = 1; + } + else + { + taggroupsize++; + } + } + +#endif + prev = node; + node = node.next; + if (node == null) + { + Console.WriteLine("Null next pointer at node {0}", count); + return false; + } + } + +#if HASHINDEX + if (underlying == null && size == 0 && taggroups != 0) + { + Console.WriteLine("Bad taggroups for empty list: size={0} taggroups={1}", size, taggroups); + retval = false; + } + if (underlying == null && size > 0) + { + oldtg = node.prev.taggroup; + if (oldtg != null) + { + if (oldtg.count != taggroupsize) + { + Console.WriteLine("Bad taggroupsize: oldtg.count ({0}) != taggroupsize ({1}) at index={2} item={3}", oldtg.count, taggroupsize, count, node.item); + retval = false; + } + + if (oldtaggroupsize <= losize && taggroupsize <= losize) + { + Console.WriteLine("Two small taggroups in a row: oldtaggroupsize ({0}), taggroupsize ({1}) at index={2} item={3}", oldtaggroupsize, taggroupsize, count, node.item); + retval = false; + } + + if (node.taggroup.tag <= oldtg.tag) + { + Console.WriteLine("Taggroup tags not strictly increasing: oldtaggrouptag ({0}), taggrouptag ({1}) at index={2} item={3}", oldtg.tag, node.taggroup.tag, count, node.item); + retval = false; + } + + if (oldtg.last != node.prev) + { + Console.WriteLine("Bad last pointer in taggroup: oldtg.last.item ({0}), node.prev.item ({1}) at index={2} item={3}", zeitem(oldtg.last), zeitem(node.prev), count, node.item); + retval = false; + } + } + + if (seentaggroups != taggroups) + { + Console.WriteLine("seentaggroups ({0}) != taggroups ({1}) (at size {2})", seentaggroups, taggroups, size); + retval = false; + } + } +#endif + if (count != size) + { + Console.WriteLine("size={0} but enumeration gives {1} nodes ", size, count); + retval = false; + } + + retval = checkViews() && retval; + +#if HASHINDEX + if (!retval) + return false; + if (underlying == null) + { + if (size != dict.Count) + { + Console.WriteLine("list.size ({0}) != dict.Count ({1})", size, dict.Count); + retval = false; + } + Node n = startsentinel.next, n2; + while (n != endsentinel) + { + if (!dict.Find(n.item, out n2)) + { + Console.WriteLine("Item in list but not dict: {0}", n.item); + retval = false; + } + else if (n != n2) + { + Console.WriteLine("Wrong node in dict for item: {0}", n.item); + retval = false; + } + n = n.next; + } + } +#endif + return retval; + } + #endregion + + #region ICloneable Members + + /// <summary> + /// Make a shallow copy of this LinkedList. + /// </summary> + /// <returns></returns> + public virtual object Clone() + { + LinkedList<T> clone = new LinkedList<T>(itemequalityComparer); + clone.AddAll(this); + return clone; + } + + #endregion + + #region System.Collections.Generic.IList<T> Members + + void System.Collections.Generic.IList<T>.RemoveAt(int index) + { + RemoveAt(index); + } + + void System.Collections.Generic.ICollection<T>.Add(T item) + { + Add(item); + } + + #endregion + + #region System.Collections.ICollection Members + + bool System.Collections.ICollection.IsSynchronized + { + get { return false; } + } + + [Obsolete] + Object System.Collections.ICollection.SyncRoot + { + // Presumably safe to use the startsentinel (of type Node, always != null) as SyncRoot + // since the class Node is private. + get { return underlying != null ? ((System.Collections.ICollection)underlying).SyncRoot : startsentinel; } + } + + void System.Collections.ICollection.CopyTo(Array arr, int index) + { + if (index < 0 || index + Count > arr.Length) + throw new ArgumentOutOfRangeException(); + + foreach (T item in this) + arr.SetValue(item, index++); + } + + #endregion + + #region System.Collections.IList Members + + Object System.Collections.IList.this[int index] + { + get { return this[index]; } + set { this[index] = (T)value; } + } + + int System.Collections.IList.Add(Object o) + { + bool added = Add((T)o); + // What position to report if item not added? SC.IList.Add doesn't say + return added ? Count-1 : -1; + } + + bool System.Collections.IList.Contains(Object o) + { + return Contains((T)o); + } + + int System.Collections.IList.IndexOf(Object o) + { + return Math.Max(-1, IndexOf((T)o)); + } + + void System.Collections.IList.Insert(int index, Object o) + { + Insert(index, (T)o); + } + + void System.Collections.IList.Remove(Object o) + { + Remove((T)o); + } + + void System.Collections.IList.RemoveAt(int index) + { + RemoveAt(index); + } + + #endregion + } }
\ No newline at end of file |