diff --git a/C5.Tests/SupportClasses.cs b/C5.Tests/SupportClasses.cs index 470342bb..6e0aaf78 100644 --- a/C5.Tests/SupportClasses.cs +++ b/C5.Tests/SupportClasses.cs @@ -8,13 +8,22 @@ namespace C5.Tests { - internal class SC : SCG.IComparer + internal class SC : SCG.IComparer, SCG.IEqualityComparer { public int Compare(string a, string b) { return a.CompareTo(b); } + public bool Equals(string x, string y) + { + return StringComparer.Ordinal.Equals(x, y); + } + + public int GetHashCode(string obj) + { + return StringComparer.Ordinal.GetHashCode(obj); + } public void appl(String s) { @@ -167,6 +176,8 @@ public override SCG.IEnumerator GetEnumerator() throw exception; } + public override bool IsReadOnly => true; + public override bool IsEmpty => false; public override int Count => contents.Length + 1; diff --git a/C5.Tests/Templates/Set.cs b/C5.Tests/Templates/Set.cs new file mode 100644 index 00000000..39349a50 --- /dev/null +++ b/C5.Tests/Templates/Set.cs @@ -0,0 +1,330 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; +using SCG = System.Collections.Generic; + +namespace C5.Tests.Templates.Set +{ + [TestFixture] + public abstract class SCG_ISetBase + { + private SCG.ISet set; + + [SetUp] + public void Init() + { + set = CreateSet(new SC(), "A", "C", "E"); + } + + [TearDown] + public void Dispose() + { + set = null; + } + + public abstract ISet CreateSet(IEqualityComparer equalityComparer, params string[] values); + + [Test] + public void Add() + { + Assert.IsTrue(set.Add("Z")); + Assert.AreEqual(4, set.Count); + Assert.IsTrue(set.Contains("Z")); + Assert.IsFalse(set.Add("A")); + } + + [Test] + public virtual void ExceptWith() + { + set.ExceptWith(new SCG.List { "C", "E", "Z" }); + Assert.AreEqual(1, set.Count); + Assert.IsTrue(set.Contains("A")); + } + + [Test] + public virtual void ExceptWith_SameEqualityComparer() + { + set.ExceptWith(new TreeSet(new SC(), new SC()) { "C", "E", "Z" }); + Assert.AreEqual(1, set.Count); + Assert.IsTrue(set.Contains("A")); + } + + [Test] + public virtual void IntersectWith() + { + set.IntersectWith(new SCG.List { "C", "E", "Z" }); + Assert.AreEqual(2, set.Count); + Assert.IsTrue(set.Contains("C")); + Assert.IsTrue(set.Contains("E")); + } + + [Test] + public virtual void IntersectWith_SameEqualityComparer() + { + set.IntersectWith(new TreeSet(new SC(), new SC()) { "C", "E", "Z" }); + Assert.AreEqual(2, set.Count); + Assert.IsTrue(set.Contains("C")); + Assert.IsTrue(set.Contains("E")); + } + + [Test] + public virtual void IsProperSubsetOf() + { + Assert.IsFalse(set.IsProperSubsetOf(new SCG.List())); + Assert.IsFalse(set.IsProperSubsetOf(new SCG.List { "C", "E", "A" })); + Assert.IsTrue(set.IsProperSubsetOf(new SCG.List { "C", "E", "A", "X" })); + Assert.IsFalse(set.IsProperSubsetOf(new SCG.List { "C", "Z" })); + set.Clear(); + Assert.IsTrue(set.IsProperSubsetOf(new SCG.List { "C", "A" })); + } + + [Test] + public virtual void IsProperSubsetOf_SameEqualityComparer() + { + Assert.IsFalse(set.IsProperSubsetOf(new TreeSet(new SC(), new SC()))); + Assert.IsFalse(set.IsProperSubsetOf(new TreeSet(new SC(), new SC()) { "C", "E", "A" })); + Assert.IsTrue(set.IsProperSubsetOf(new TreeSet(new SC(), new SC()) { "C", "E", "A", "X" })); + Assert.IsFalse(set.IsProperSubsetOf(new TreeSet(new SC(), new SC()) { "C", "Z" })); + set.Clear(); + Assert.IsTrue(set.IsProperSubsetOf(new TreeSet(new SC(), new SC()) { "C", "A" })); + } + + [Test] + public virtual void IsProperSupersetOf() + { + Assert.IsTrue(set.IsProperSupersetOf(new SCG.List())); + Assert.IsFalse(set.IsProperSupersetOf(new SCG.List { "C", "E", "A" })); + Assert.IsTrue(set.IsProperSupersetOf(new SCG.List { "C", "A" })); + Assert.IsFalse(set.IsProperSupersetOf(new SCG.List { "C", "Z" })); + set.Clear(); + Assert.IsFalse(set.IsProperSupersetOf(new SCG.List { "C", "A" })); + } + + [Test] + public virtual void IsProperSupersetOf_SameEqualityComparer() + { + Assert.IsTrue(set.IsProperSupersetOf(new TreeSet(new SC(), new SC()))); + Assert.IsFalse(set.IsProperSupersetOf(new TreeSet(new SC(), new SC()) { "C", "E", "A" })); + Assert.IsTrue(set.IsProperSupersetOf(new TreeSet(new SC(), new SC()) { "C", "A" })); + Assert.IsFalse(set.IsProperSupersetOf(new TreeSet(new SC(), new SC()) { "C", "Z" })); + set.Clear(); + Assert.IsFalse(set.IsProperSupersetOf(new TreeSet(new SC(), new SC()) { "C", "A" })); + } + + [Test] + public virtual void IsSubsetOf() + { + Assert.IsFalse(set.IsSubsetOf(new SCG.List())); + Assert.IsTrue(set.IsSubsetOf(new SCG.List { "C", "E", "A" })); + Assert.IsTrue(set.IsSubsetOf(new SCG.List { "C", "E", "A", "X" })); + Assert.IsFalse(set.IsSubsetOf(new SCG.List { "C", "Z" })); + Assert.IsFalse(set.IsSubsetOf(new SCG.List { "C", "A", "Z" })); + set.Clear(); + Assert.IsTrue(set.IsSubsetOf(new SCG.List { "C", "A" })); + } + + [Test] + public virtual void IsSubsetOf_SameEqualityComparer() + { + Assert.IsFalse(set.IsSubsetOf(new TreeSet(new SC(), new SC()))); + Assert.IsTrue(set.IsSubsetOf(new TreeSet(new SC(), new SC()) { "C", "E", "A" })); + Assert.IsTrue(set.IsSubsetOf(new TreeSet(new SC(), new SC()) { "C", "E", "A", "X" })); + Assert.IsFalse(set.IsSubsetOf(new TreeSet(new SC(), new SC()) { "C", "Z" })); + Assert.IsFalse(set.IsSubsetOf(new TreeSet(new SC(), new SC()) { "C", "A", "Z" })); + set.Clear(); + Assert.IsTrue(set.IsSubsetOf(new TreeSet(new SC(), new SC()) { "C", "A" })); + } + + [Test] + public virtual void IsSupersetOf() + { + Assert.IsTrue(set.IsSupersetOf(new SCG.List())); + Assert.IsTrue(set.IsSupersetOf(new SCG.List { "C", "E", "A" })); + Assert.IsFalse(set.IsSupersetOf(new SCG.List { "C", "E", "A", "X" })); + Assert.IsFalse(set.IsSupersetOf(new SCG.List { "C", "Z" })); + Assert.IsTrue(set.IsSupersetOf(new SCG.List { "C", "A" })); + set.Clear(); + Assert.IsFalse(set.IsSupersetOf(new SCG.List { "C", "A" })); + } + + [Test] + public virtual void IsSupersetOf_SameEqualityComparer() + { + Assert.IsTrue(set.IsSupersetOf(new TreeSet(new SC(), new SC()))); + Assert.IsTrue(set.IsSupersetOf(new TreeSet(new SC(), new SC()) { "C", "E", "A" })); + Assert.IsFalse(set.IsSupersetOf(new TreeSet(new SC(), new SC()) { "C", "E", "A", "X" })); + Assert.IsFalse(set.IsSupersetOf(new TreeSet(new SC(), new SC()) { "C", "Z" })); + Assert.IsTrue(set.IsSupersetOf(new TreeSet(new SC(), new SC()) { "C", "A" })); + set.Clear(); + Assert.IsFalse(set.IsSupersetOf(new TreeSet(new SC(), new SC()) { "C", "A" })); + } + + [Test] + public virtual void Overlaps() + { + Assert.IsFalse(set.Overlaps(new SCG.List())); + Assert.IsTrue(set.Overlaps(new SCG.List { "C", "E", "A" })); + Assert.IsTrue(set.Overlaps(new SCG.List { "C", "E", "A", "X" })); + Assert.IsFalse(set.Overlaps(new SCG.List { "X", "Z" })); + Assert.IsTrue(set.Overlaps(new SCG.List { "C", "A" })); + set.Clear(); + Assert.IsFalse(set.Overlaps(new SCG.List { "C", "A" })); + } + + [Test] + public virtual void Overlaps_SameEqualityComparer() + { + Assert.IsFalse(set.Overlaps(new TreeSet(new SC(), new SC()))); + Assert.IsTrue(set.Overlaps(new TreeSet(new SC(), new SC()) { "C", "E", "A" })); + Assert.IsTrue(set.Overlaps(new TreeSet(new SC(), new SC()) { "C", "E", "A", "X" })); + Assert.IsFalse(set.Overlaps(new TreeSet(new SC(), new SC()) { "X", "Z" })); + Assert.IsTrue(set.Overlaps(new TreeSet(new SC(), new SC()) { "C", "A" })); + set.Clear(); + Assert.IsFalse(set.Overlaps(new TreeSet(new SC(), new SC()) { "C", "A" })); + } + + [Test] + public virtual void SetEquals() + { + Assert.IsFalse(set.SetEquals(new SCG.List())); + Assert.IsTrue(set.SetEquals(new SCG.List { "C", "E", "A" })); + Assert.IsFalse(set.SetEquals(new SCG.List { "C", "E", "A", "X" })); + Assert.IsFalse(set.SetEquals(new SCG.List { "X", "Z" })); + Assert.IsFalse(set.SetEquals(new SCG.List { "C", "A" })); + set.Clear(); + Assert.IsFalse(set.SetEquals(new SCG.List { "C", "A" })); + Assert.IsTrue(set.SetEquals(new SCG.List())); + } + + [Test] + public virtual void SetEquals_SameEqualityComparer() + { + Assert.IsFalse(set.SetEquals(new TreeSet(new SC(), new SC()))); + Assert.IsTrue(set.SetEquals(new TreeSet(new SC(), new SC()) { "C", "E", "A" })); + Assert.IsFalse(set.SetEquals(new TreeSet(new SC(), new SC()) { "C", "E", "A", "X" })); + Assert.IsFalse(set.SetEquals(new TreeSet(new SC(), new SC()) { "X", "Z" })); + Assert.IsFalse(set.SetEquals(new TreeSet(new SC(), new SC()) { "C", "A" })); + set.Clear(); + Assert.IsFalse(set.SetEquals(new TreeSet(new SC(), new SC()) { "C", "A" })); + Assert.IsTrue(set.SetEquals(new TreeSet(new SC(), new SC()))); + } + + [Test] + public virtual void SymmetricExceptWith() + { + set.SymmetricExceptWith(new SCG.List()); + Assert.AreEqual(3, set.Count); + set.SymmetricExceptWith(new SCG.List { "C", "E", "R", "X" }); + Assert.AreEqual(3, set.Count); + Assert.IsTrue(set.SetEquals(new SCG.List { "A", "R", "X" })); + set.SymmetricExceptWith(new SCG.List { "A", "R", "X" }); + Assert.AreEqual(0, set.Count); + + set.Clear(); + set.SymmetricExceptWith(new SCG.List { "C", "E", "A" }); + Assert.IsTrue(set.SetEquals(new SCG.List { "C", "E", "A" })); + } + + [Test] + public virtual void SymmetricExceptWith_SameEqualityComparer() + { + set.SymmetricExceptWith(new TreeSet(new SC(), new SC())); + Assert.AreEqual(3, set.Count); + set.SymmetricExceptWith(new TreeSet(new SC(), new SC()) { "C", "E", "R", "X" }); + Assert.AreEqual(3, set.Count); + Assert.IsTrue(set.SetEquals(new SCG.List { "A", "R", "X" })); + set.SymmetricExceptWith(new TreeSet(new SC(), new SC()) { "A", "R", "X" }); + Assert.AreEqual(0, set.Count); + + set.Clear(); + set.SymmetricExceptWith(new TreeSet(new SC(), new SC()) { "C", "E", "A" }); + Assert.IsTrue(set.SetEquals(new SCG.List { "C", "E", "A" })); + } + + [Test] + public virtual void UnionWith() + { + set.UnionWith(new SCG.List()); + Assert.AreEqual(3, set.Count); + set.UnionWith(new SCG.List { "C", "E", "R", "X" }); + Assert.AreEqual(5, set.Count); + Assert.IsTrue(set.SetEquals(new SCG.List { "A", "C", "E", "R", "X" })); + set.UnionWith(new SCG.List { "A", "R", "X" }); + Assert.AreEqual(5, set.Count); + Assert.IsTrue(set.SetEquals(new SCG.List { "A", "C", "E", "R", "X" })); + + set.Clear(); + set.UnionWith(new SCG.List { "C", "E", "A" }); + Assert.IsTrue(set.SetEquals(new SCG.List { "C", "E", "A" })); + } + + [Test] + public virtual void UnionWith_SameEqualityComparer() + { + set.UnionWith(new TreeSet(new SC(), new SC())); + Assert.AreEqual(3, set.Count); + set.UnionWith(new TreeSet(new SC(), new SC()) { "C", "E", "R", "X" }); + Assert.AreEqual(5, set.Count); + Assert.IsTrue(set.SetEquals(new SCG.List { "A", "C", "E", "R", "X" })); + set.UnionWith(new TreeSet(new SC(), new SC()) { "A", "R", "X" }); + Assert.AreEqual(5, set.Count); + Assert.IsTrue(set.SetEquals(new SCG.List { "A", "C", "E", "R", "X" })); + + set.Clear(); + set.UnionWith(new TreeSet(new SC(), new SC()) { "C", "E", "A" }); + Assert.IsTrue(set.SetEquals(new SCG.List { "C", "E", "A" })); + } + + // ICollection members + [Test] + public virtual void Clear() + { + Assert.AreEqual(3, set.Count); + set.Clear(); + Assert.AreEqual(0, set.Count); + } + + [Test] + public virtual void Contains() + { + Assert.IsTrue(set.Contains("A")); + Assert.IsFalse(set.Contains("Z")); + } + + [Test] + public virtual void CopyTo() + { + var values = new string[set.Count + 2]; + set.CopyTo(values, 1); + Assert.AreEqual(null, values[0]); + Assert.AreEqual("A", values[1]); + Assert.AreEqual("C", values[2]); + Assert.AreEqual("E", values[3]); + Assert.AreEqual(null, values[4]); + } + + [Test] + public virtual void Remove() + { + Assert.AreEqual(3, set.Count); + Assert.IsTrue(set.Remove("A")); + Assert.AreEqual(2, set.Count); + Assert.IsFalse(set.Remove("A")); + Assert.AreEqual(2, set.Count); + } + + [Test] + public virtual void Count() + { + Assert.AreEqual(3, set.Count); + set.Add("Foo"); + Assert.AreEqual(4, set.Count); + } + + [Test] + public virtual void IsReadOnly() + { + Assert.AreEqual(false, set.IsReadOnly); + } + } +} diff --git a/C5.Tests/Trees/Dictionary.cs b/C5.Tests/Trees/Dictionary.cs index 9cefb3b9..d4a82b2e 100644 --- a/C5.Tests/Trees/Dictionary.cs +++ b/C5.Tests/Trees/Dictionary.cs @@ -490,4 +490,264 @@ public void Dispose() } } } + + [TestFixture] + public class SCGIDictionary + { + private SCG.IDictionary dict; + + [SetUp] + public void Init() + { + dict = new TreeDictionary(new SC()) + { + { "A", "1" }, + { "C", "2" }, + { "E", "3" } + }; + } + + [TearDown] + public void Dispose() { dict = null; } + + [Test] + public void Add() + { + Assert.AreEqual(3, dict.Count); + dict.Add("S", "4"); + Assert.AreEqual(4, dict.Count); + } + + [Test] + public void ContainsKey() + { + Assert.IsTrue(dict.ContainsKey("A")); + Assert.IsFalse(dict.ContainsKey("Z")); + } + + [Test] + public void Remove() + { + Assert.AreEqual(3, dict.Count); + Assert.IsTrue(dict.Remove("A")); + Assert.AreEqual(2, dict.Count); + Assert.IsFalse(dict.Remove("Z")); + Assert.AreEqual(2, dict.Count); + } + + [Test] + public void TryGetValue() + { + Assert.IsTrue(dict.TryGetValue("C", out string value)); + Assert.AreEqual("2", value); + Assert.IsFalse(dict.TryGetValue("Z", out value)); + Assert.IsNull(value); + } + + [Test] + public void GetItem() + { + Assert.AreEqual("3", dict["E"]); + Assert.Throws(() => { var x = dict["Z"]; }); + } + + [Test] + public void SetItem() + { + dict["C"] = "9"; + Assert.AreEqual("9", dict["C"]); + dict["Z"] = "5"; + Assert.AreEqual("5", dict["Z"]); + } + + // ICollection> + [Test] + public void Clear() + { + dict.Clear(); + Assert.AreEqual(0, dict.Count); + } + + [Test] + public void Contains() + { + Assert.IsTrue(dict.Contains(new SCG.KeyValuePair("C", "2"))); + Assert.IsFalse(dict.Contains(new SCG.KeyValuePair("D", "2"))); + Assert.IsFalse(dict.Contains(new SCG.KeyValuePair("C", "6"))); + } + + [Test] + public void CopyTo() + { + var pairs = new SCG.KeyValuePair[dict.Count]; + dict.CopyTo(pairs, 0); + Assert.AreEqual("C", pairs[1].Key); + Assert.AreEqual("2", pairs[1].Value); + Assert.AreEqual("E", pairs[2].Key); + Assert.AreEqual("3", pairs[2].Value); + } + + [Test] + public void RemovePair() + { + Assert.AreEqual(3, dict.Count); + Assert.IsTrue(dict.Remove(new SCG.KeyValuePair("A", "1"))); + Assert.AreEqual(2, dict.Count); + Assert.IsFalse(dict.Remove(new SCG.KeyValuePair("Z", "9"))); + Assert.AreEqual(2, dict.Count); + } + + [Test] + public void Count() + { + Assert.AreEqual(3, dict.Count); + } + + [Test] + public void IsReadOnly() + { + Assert.AreEqual(false, dict.IsReadOnly); + } + + [Test] + public void GetEnumerable() + { + var enumerable = dict.GetEnumerator(); + Assert.IsTrue(enumerable.MoveNext()); + Assert.AreEqual(new SCG.KeyValuePair("A", "1"), enumerable.Current); + Assert.IsTrue(enumerable.MoveNext()); + Assert.AreEqual(new SCG.KeyValuePair("C", "2"), enumerable.Current); + Assert.IsTrue(enumerable.MoveNext()); + Assert.AreEqual(new SCG.KeyValuePair("E", "3"), enumerable.Current); + Assert.IsFalse(enumerable.MoveNext()); + } + + [Test] + public void Keys_GetEnumerable() + { + var enumerable = dict.Keys.GetEnumerator(); + Assert.IsTrue(enumerable.MoveNext()); + Assert.AreEqual("A", enumerable.Current); + Assert.IsTrue(enumerable.MoveNext()); + Assert.AreEqual("C", enumerable.Current); + Assert.IsTrue(enumerable.MoveNext()); + Assert.AreEqual("E", enumerable.Current); + Assert.IsFalse(enumerable.MoveNext()); + } + + [Test] + public void Keys_Count() + { + Assert.AreEqual(3, dict.Keys.Count); + dict.Remove("C"); + Assert.AreEqual(2, dict.Keys.Count); + } + + [Test] + public void Keys_IsReadOnly() + { + Assert.AreEqual(true, dict.Keys.IsReadOnly); + } + + [Test] + public void Keys_Add() + { + Assert.Throws(() => dict.Keys.Add("Foo")); + } + + [Test] + public void Keys_Clear() + { + Assert.Throws(() => dict.Keys.Clear()); + } + + [Test] + public void Keys_Contains() + { + Assert.IsTrue(dict.Keys.Contains("A")); + Assert.IsFalse(dict.Keys.Contains("B")); + } + + [Test] + public void Keys_CopyTo() + { + var keys = new string[dict.Keys.Count + 2]; + dict.Keys.CopyTo(keys, 1); + Assert.AreEqual(null, keys[0]); + Assert.AreEqual("A", keys[1]); + Assert.AreEqual("C", keys[2]); + Assert.AreEqual("E", keys[3]); + Assert.AreEqual(null, keys[4]); + } + + [Test] + public void Keys_Remove() + { + Assert.Throws(() => dict.Keys.Remove("Foo")); + } + + [Test] + public void Values_GetEnumerable() + { + var enumerable = dict.Values.GetEnumerator(); + Assert.IsTrue(enumerable.MoveNext()); + Assert.AreEqual("1", enumerable.Current); + Assert.IsTrue(enumerable.MoveNext()); + Assert.AreEqual("2", enumerable.Current); + Assert.IsTrue(enumerable.MoveNext()); + Assert.AreEqual("3", enumerable.Current); + Assert.IsFalse(enumerable.MoveNext()); + } + + [Test] + public void Values_Count() + { + Assert.AreEqual(3, dict.Values.Count); + dict.Remove("C"); + Assert.AreEqual(2, dict.Values.Count); + } + + [Test] + public void Values_IsReadOnly() + { + Assert.AreEqual(true, dict.Values.IsReadOnly); + } + + [Test] + public void Values_Add() + { + Assert.Throws(() => dict.Values.Add("Foo")); + } + + [Test] + public void Values_Clear() + { + Assert.Throws(() => dict.Values.Clear()); + } + + [Test] + public void Values_Contains() + { + Assert.IsTrue(dict.Values.Contains("1")); + Assert.IsFalse(dict.Values.Contains("9")); + } + + [Test] + public void Values_CopyTo() + { + var values = new string[dict.Values.Count + 2]; + dict.Values.CopyTo(values, 1); + Assert.AreEqual(null, values[0]); + Assert.AreEqual("1", values[1]); + Assert.AreEqual("2", values[2]); + Assert.AreEqual("3", values[3]); + Assert.AreEqual(null, values[4]); + } + + [Test] + public void Values_Remove() + { + Assert.Throws(() => dict.Values.Remove("1")); + } + } } diff --git a/C5.Tests/Trees/RedBlackTreeSetTests.cs b/C5.Tests/Trees/RedBlackTreeSetTests.cs index e39d252c..209f4c6b 100644 --- a/C5.Tests/Trees/RedBlackTreeSetTests.cs +++ b/C5.Tests/Trees/RedBlackTreeSetTests.cs @@ -1,8 +1,10 @@ // This file is part of the C5 Generic Collection Library for C# and CLI // See https://github.com/sestoft/C5/blob/master/LICENSE.txt for licensing details. +using C5.Tests.Templates.Set; using NUnit.Framework; using System; +using System.Collections.Generic; using SCG = System.Collections.Generic; namespace C5.Tests.trees.TreeSet @@ -133,7 +135,7 @@ public void UpdateOrAdd1() [Test] public void UpdateOrAdd2() { - ICollection coll = new TreeSet(); + ICollection coll = new TreeSet(); // s1 and s2 are distinct objects but contain the same text: String s1 = "abc", s2 = ("def" + s1).Substring(3); Assert.IsFalse(coll.UpdateOrAdd(s1, out string old)); @@ -3039,4 +3041,14 @@ public void Dispose() } } + + public class SCG_ISet : SCG_ISetBase + { + public override ISet CreateSet(IEqualityComparer equalityComparer, params string[] values) + { + var set = new TreeSet(SCG.Comparer.Default, equalityComparer); + set.UnionWith(values); + return set; + } + } } \ No newline at end of file diff --git a/C5.UserGuideExamples/Graph.cs b/C5.UserGuideExamples/Graph.cs index 0256be8f..23f150c2 100644 --- a/C5.UserGuideExamples/Graph.cs +++ b/C5.UserGuideExamples/Graph.cs @@ -573,6 +573,8 @@ internal EdgesValue(HashGraph g) _graph = g; } + public override bool IsReadOnly => true; + public override bool IsEmpty => _graph.EdgeCount == 0; public override int Count => _graph.EdgeCount; diff --git a/C5.UserGuideExamples/MultiCollection.cs b/C5.UserGuideExamples/MultiCollection.cs index e7460d34..e7f7d5ee 100644 --- a/C5.UserGuideExamples/MultiCollection.cs +++ b/C5.UserGuideExamples/MultiCollection.cs @@ -22,6 +22,8 @@ public BasicCollectionValue(SCG.IEnumerable e, Func chooser, int c) Count = c; } + public override bool IsReadOnly => true; + public override int Count { get; } public override Speed CountSpeed => Speed.Constant; diff --git a/C5/Arrays/ArrayList.cs b/C5/Arrays/ArrayList.cs index de323b2f..3a8eabb1 100644 --- a/C5/Arrays/ArrayList.cs +++ b/C5/Arrays/ArrayList.cs @@ -1481,7 +1481,7 @@ public override bool UnsequencedEquals(ICollection that) /// /// The value to check for. /// True if the items is in this collection. - public virtual bool Contains(T item) + public override bool Contains(T item) { ValidityCheck(); return IndexOfInner(item) >= 0; } @@ -1611,7 +1611,7 @@ public virtual bool UpdateOrAdd(T item, out T olditem) /// /// The value to remove. /// True if the item was found (and removed). - public virtual bool Remove(T item) + public override bool Remove(T item) { UpdateCheck(); @@ -2142,7 +2142,7 @@ public override bool Check() /// /// The item to add. /// True - public virtual bool Add(T item) + public override bool Add(T item) { UpdateCheck(); diff --git a/C5/BaseClasses/ArrayBase.cs b/C5/BaseClasses/ArrayBase.cs index c6873581..6e010f82 100644 --- a/C5/BaseClasses/ArrayBase.cs +++ b/C5/BaseClasses/ArrayBase.cs @@ -125,7 +125,7 @@ protected ArrayBase(int capacity, System.Collections.Generic.IEqualityComparer /// Remove all items and reset size of internal array container. /// - public virtual void Clear() + public override void Clear() { UpdateCheck(); array = new T[8]; @@ -238,6 +238,10 @@ internal Range(ArrayBase thebase, int start, int count, bool forwards) /// True if this collection is empty. public override bool IsEmpty { get { thebase.ModifyCheck(stamp); return count == 0; } } + /// + /// Gets a value indicating whether the is read-only. Always returns true. + /// + public override bool IsReadOnly => true; /// /// diff --git a/C5/BaseClasses/CollectionBase.cs b/C5/BaseClasses/CollectionBase.cs index 0ecef724..c4eb3435 100644 --- a/C5/BaseClasses/CollectionBase.cs +++ b/C5/BaseClasses/CollectionBase.cs @@ -287,7 +287,7 @@ protected virtual void UpdateCheck() /// /// /// True if this collection is read only - public virtual bool IsReadOnly => isReadOnlyBase; + public override bool IsReadOnly => isReadOnlyBase; #endregion diff --git a/C5/BaseClasses/CollectionValueBase.cs b/C5/BaseClasses/CollectionValueBase.cs index 81911ff5..2c06cc14 100644 --- a/C5/BaseClasses/CollectionValueBase.cs +++ b/C5/BaseClasses/CollectionValueBase.cs @@ -653,5 +653,48 @@ public override string ToString() { return ToString(null, null); } + + #region SCG.ICollection Members + + /// + /// Gets a value indicating whether the is read-only. + /// + public abstract bool IsReadOnly { get; } + + /// + /// Adds an item to the . + /// + /// The default implementation throws a . Override to provide an implementation. + /// + /// The object to add to the . + public virtual bool Add(T item) => throw new ReadOnlyCollectionException(); + + void System.Collections.Generic.ICollection.Add(T item) => this.Add(item); + + /// + /// Removes all items from the . + /// + /// The default implementation throws a . Override to provide an implementation. + /// + public virtual void Clear() => throw new ReadOnlyCollectionException(); + + /// + /// Determines whether the contains a specific value. + /// + /// The object to locate in the . + /// true if item is found in the ; otherwise, false. + public virtual bool Contains(T item) => this.Exists((thisItem) => EqualityComparer.Default.Equals(thisItem, item)); + + /// + /// Removes the first occurrence of a specific object from the . + /// + /// The default implementation throws a . Override to provide an implementation. + /// + /// The object to remove from the . + /// true if item was successfully removed from the ; otherwise, false. + /// This method also returns false if item is not found in the original . + public virtual bool Remove(T item) => throw new ReadOnlyCollectionException(); + + #endregion } } \ No newline at end of file diff --git a/C5/BaseClasses/DictionaryBase.cs b/C5/BaseClasses/DictionaryBase.cs index 832e6699..0a82202c 100644 --- a/C5/BaseClasses/DictionaryBase.cs +++ b/C5/BaseClasses/DictionaryBase.cs @@ -1,5 +1,5 @@ using System; -using System.Collections.Generic; +using SCG = System.Collections.Generic; namespace C5 { @@ -9,7 +9,7 @@ namespace C5 /// /// [Serializable] - public abstract class DictionaryBase : CollectionValueBase>, IDictionary + public abstract class DictionaryBase : CollectionValueBase>, IDictionary, SCG.IDictionary { /// /// The set collection of entries underlying this dictionary implementation @@ -186,7 +186,7 @@ public virtual bool Remove(K key, out V value) /// /// Remove all entries from the dictionary /// - public virtual void Clear() { pairs.Clear(); } + public override void Clear() { pairs.Clear(); } /// /// @@ -207,7 +207,7 @@ public virtual bool Contains(K key) } [Serializable] - private class LiftedEnumerable : IEnumerable> where H : K + private class LiftedEnumerable : SCG.IEnumerable> where H : K { private readonly System.Collections.Generic.IEnumerable keys; public LiftedEnumerable(System.Collections.Generic.IEnumerable keys) { this.keys = keys; } @@ -379,6 +379,8 @@ public override System.Collections.Generic.IEnumerator GetEnumerator() public override int Count => pairs.Count; public override Speed CountSpeed => Speed.Constant; + + public override bool IsReadOnly => true; } [Serializable] @@ -405,6 +407,8 @@ public override System.Collections.Generic.IEnumerator GetEnumerator() public override int Count => pairs.Count; public override Speed CountSpeed => pairs.CountSpeed; + + public override bool IsReadOnly => true; } #endregion @@ -455,8 +459,8 @@ public virtual V this[K key] /// /// /// - /// True if dictionary is read only - public virtual bool IsReadOnly => pairs.IsReadOnly; + /// True if dictionary is read only + public override bool IsReadOnly => pairs.IsReadOnly; /// @@ -517,5 +521,83 @@ public override bool Show(System.Text.StringBuilder stringbuilder, ref int rest, { return Showing.ShowDictionary(this, stringbuilder, ref rest, formatProvider); } + + #region SCG.IDictionary Members + + SCG.ICollection SCG.IDictionary.Keys => this.Keys; + + SCG.ICollection SCG.IDictionary.Values => this.Values; + + int SCG.ICollection>.Count => this.Count; + + bool SCG.ICollection>.IsReadOnly => this.IsReadOnly; + + V SCG.IDictionary.this[K key] { get => this[key]; set => this[key] = value; } + + void SCG.IDictionary.Add(K key, V value) + { + this.Add(key, value); + } + + bool SCG.IDictionary.ContainsKey(K key) + { + return this.Contains(key); + } + + bool SCG.IDictionary.Remove(K key) + { + return this.Remove(key); + } + + bool SCG.IDictionary.TryGetValue(K key, out V value) + { + SCG.KeyValuePair p = new SCG.KeyValuePair(key, default); + + bool result = pairs.Find(ref p); + value = p.Value; + return result; + } + + void SCG.ICollection>.Add(SCG.KeyValuePair item) + { + this.Add(item.Key, item.Value); + } + + void SCG.ICollection>.Clear() + { + this.Clear(); + } + + bool SCG.ICollection>.Contains(SCG.KeyValuePair item) + { + foreach (var thisItem in this) + if (thisItem.Equals(item)) + return true; + + return false; + } + + void SCG.ICollection>.CopyTo(SCG.KeyValuePair[] array, int arrayIndex) + { + if (arrayIndex < 0 || arrayIndex + Count > array.Length) + throw new ArgumentOutOfRangeException(nameof(arrayIndex)); + + foreach (var item in this) + array[arrayIndex++] = new SCG.KeyValuePair(item.Key, item.Value); + } + + bool SCG.ICollection>.Remove(SCG.KeyValuePair item) + { + return this.Remove(item.Key); + } + + SCG.IEnumerator> SCG.IEnumerable>.GetEnumerator() + { + foreach (var item in this) + yield return new SCG.KeyValuePair(item.Key, item.Value); + } + + #endregion + } } \ No newline at end of file diff --git a/C5/Dictionaries/SortedDictionaryBase.cs b/C5/Dictionaries/SortedDictionaryBase.cs index 17e23aa8..88f5b825 100644 --- a/C5/Dictionaries/SortedDictionaryBase.cs +++ b/C5/Dictionaries/SortedDictionaryBase.cs @@ -424,7 +424,7 @@ public IDirectedCollectionValue RangeAll() #region ICollection Members public Speed ContainsSpeed => sorteddict.ContainsSpeed; - public bool Contains(K key) { return sorteddict.Contains(key); ; } + public override bool Contains(K key) { return sorteddict.Contains(key); } public int ContainsCount(K item) { return sorteddict.Contains(item) ? 1 : 0; } @@ -479,7 +479,7 @@ public bool Find(ref K item) public bool UpdateOrAdd(K item, out K olditem) { throw new ReadOnlyCollectionException(); } - public bool Remove(K item) { throw new ReadOnlyCollectionException(); } + public override bool Remove(K item) { throw new ReadOnlyCollectionException(); } public bool Remove(K item, out K removeditem) { throw new ReadOnlyCollectionException(); } @@ -487,7 +487,7 @@ public bool Find(ref K item) public void RemoveAll(SCG.IEnumerable items) { throw new ReadOnlyCollectionException(); } - public void Clear() { throw new ReadOnlyCollectionException(); } + public override void Clear() { throw new ReadOnlyCollectionException(); } public void RetainAll(SCG.IEnumerable items) { throw new ReadOnlyCollectionException(); } @@ -500,7 +500,7 @@ public bool Find(ref K item) public bool DuplicatesByCounting => true; - public bool Add(K item) { throw new ReadOnlyCollectionException(); } + public override bool Add(K item) { throw new ReadOnlyCollectionException(); } void SCG.ICollection.Add(K item) { throw new ReadOnlyCollectionException(); } diff --git a/C5/Enumerators/MappedCollectionValue.cs b/C5/Enumerators/MappedCollectionValue.cs index 61228ba1..c9f3ca89 100644 --- a/C5/Enumerators/MappedCollectionValue.cs +++ b/C5/Enumerators/MappedCollectionValue.cs @@ -22,6 +22,8 @@ protected MappedCollectionValue(ICollectionValue collectionValue) public override Speed CountSpeed => collectionValue.CountSpeed; + public override bool IsReadOnly => collectionValue.IsReadOnly; + public override System.Collections.Generic.IEnumerator GetEnumerator() { foreach (var item in collectionValue) diff --git a/C5/Enumerators/MappedDirectedCollectionValue.cs b/C5/Enumerators/MappedDirectedCollectionValue.cs index d7473df5..27b2a69f 100644 --- a/C5/Enumerators/MappedDirectedCollectionValue.cs +++ b/C5/Enumerators/MappedDirectedCollectionValue.cs @@ -26,6 +26,8 @@ protected MappedDirectedCollectionValue(IDirectedCollectionValue directedColl public override Speed CountSpeed => directedCollectionValue.CountSpeed; + public override bool IsReadOnly => directedCollectionValue.IsReadOnly; + public override IDirectedCollectionValue Backwards() { var ret = (MappedDirectedCollectionValue)MemberwiseClone(); diff --git a/C5/Hashing/HashBag.cs b/C5/Hashing/HashBag.cs index 3cb09c13..46c72b38 100644 --- a/C5/Hashing/HashBag.cs +++ b/C5/Hashing/HashBag.cs @@ -81,7 +81,7 @@ public HashBag(int capacity, double fill, SCG.IEqualityComparer itemequalityC /// /// The item to look for /// True if bag contains item - public virtual bool Contains(T item) + public override bool Contains(T item) { return dict.Contains(new System.Collections.Generic.KeyValuePair(item, 0)); } @@ -217,7 +217,7 @@ public virtual bool UpdateOrAdd(T item, out T olditem) /// /// The item to remove /// True if item was (found and) removed - public virtual bool Remove(T item) + public override bool Remove(T item) { var p = new SCG.KeyValuePair(item, 0); @@ -321,7 +321,7 @@ public virtual void RemoveAll(SCG.IEnumerable items) /// /// Remove all items from the bag, resetting internal table to initial size. /// - public virtual void Clear() + public override void Clear() { UpdateCheck(); if (size == 0) @@ -569,7 +569,7 @@ public override void CopyTo(T[] array, int index) /// /// The item to add. /// Always true - public virtual bool Add(T item) + public override bool Add(T item) { UpdateCheck(); Add(ref item); diff --git a/C5/Hashing/HashSet.cs b/C5/Hashing/HashSet.cs index 434a0dde..e01bf47b 100644 --- a/C5/Hashing/HashSet.cs +++ b/C5/Hashing/HashSet.cs @@ -451,7 +451,7 @@ public virtual bool UpdateOrAdd(T item, out T olditem) /// /// The item to remove /// True if item was (found and) removed - public virtual bool Remove(T item) + public override bool Remove(T item) { UpdateCheck(); if (Remove(ref item)) @@ -515,7 +515,7 @@ public virtual void RemoveAll(SCG.IEnumerable items) /// /// Remove all items from the set, resetting internal table to initial size. /// - public virtual void Clear() + public override void Clear() { UpdateCheck(); int oldsize = size; @@ -743,7 +743,7 @@ public override SCG.IEnumerator GetEnumerator() /// /// The item to add. /// True if item was added (i.e. not found) - public virtual bool Add(T item) + public override bool Add(T item) { UpdateCheck(); return !SearchOrAdd(ref item, true, false, true); @@ -753,10 +753,7 @@ public virtual bool Add(T item) /// Add an item to this set. /// /// The item to add. - void SCG.ICollection.Add(T item) - { - Add(item); - } + void SCG.ICollection.Add(T item) => Add(item); /// /// Add the elements from another collection with a more specialized item type diff --git a/C5/Heaps/IntervalHeap.cs b/C5/Heaps/IntervalHeap.cs index 10891f5c..ef253901 100644 --- a/C5/Heaps/IntervalHeap.cs +++ b/C5/Heaps/IntervalHeap.cs @@ -332,7 +332,7 @@ public T DeleteMax() /// ReadOnlyCollectionException /// /// True if this collection is read-only. - public bool IsReadOnly => false; + public override bool IsReadOnly => false; /// /// diff --git a/C5/Interfaces/ICollectionValue.cs b/C5/Interfaces/ICollectionValue.cs index a05c83a5..5050b132 100644 --- a/C5/Interfaces/ICollectionValue.cs +++ b/C5/Interfaces/ICollectionValue.cs @@ -10,8 +10,17 @@ namespace C5 /// collection. The main usage for this interface is to be the return type of /// query operations on generic collection. /// - public interface ICollectionValue : IEnumerable, IShowable + public interface ICollectionValue : IEnumerable, IShowable, System.Collections.Generic.ICollection { + /// + /// 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. + /// + /// The item to add. + /// True if item was added. + new bool Add(T item); + /// /// A flag bitmap of the events subscribable to by this collection. /// @@ -63,7 +72,7 @@ public interface ICollectionValue : IEnumerable, IShowable /// /// /// The number of items in this collection - int Count { get; } + new int Count { get; } /// /// The value is symbolic indicating the type of asymptotic complexity @@ -79,7 +88,7 @@ public interface ICollectionValue : IEnumerable, IShowable /// /// The array to copy to /// The index at which to copy the first item - void CopyTo(T[] array, int index); + new void CopyTo(T[] array, int index); /// /// Create an array with the items of this collection (in the same order as an diff --git a/C5/Interfaces/IDictionary.cs b/C5/Interfaces/IDictionary.cs index 26bd0bd9..60f91165 100644 --- a/C5/Interfaces/IDictionary.cs +++ b/C5/Interfaces/IDictionary.cs @@ -27,7 +27,7 @@ public interface IDictionary : ICollectionValue /// True if dictionary is read-only - bool IsReadOnly { get; } + new bool IsReadOnly { get; } /// @@ -110,7 +110,7 @@ void AddAll(IEnumerable> ent /// /// Remove all entries from the dictionary /// - void Clear(); + new void Clear(); /// diff --git a/C5/Interfaces/IExtensible.cs b/C5/Interfaces/IExtensible.cs index 48765ade..f2897847 100644 --- a/C5/Interfaces/IExtensible.cs +++ b/C5/Interfaces/IExtensible.cs @@ -14,7 +14,7 @@ public interface IExtensible : ICollectionValue /// ReadOnlyCollectionException /// /// True if this collection is read-only. - bool IsReadOnly { get; } + new bool IsReadOnly { get; } //TODO: wonder where the right position of this is /// @@ -49,7 +49,7 @@ public interface IExtensible : ICollectionValue /// /// The item to add. /// True if item was added. - bool Add(T item); + new bool Add(T item); /// /// Add the elements from another collection with a more specialized item type diff --git a/C5/LinkedLists/HashedLinkedList.cs b/C5/LinkedLists/HashedLinkedList.cs index 89fcbddd..2a232233 100644 --- a/C5/LinkedLists/HashedLinkedList.cs +++ b/C5/LinkedLists/HashedLinkedList.cs @@ -1119,6 +1119,8 @@ internal Range(HashedLinkedList list, int start, int count, bool forwards) } } + public override bool IsReadOnly => true; + public override bool IsEmpty { get { list.ModifyCheck(rangestamp); return count == 0; } } public override int Count { get { list.ModifyCheck(rangestamp); return count; } } diff --git a/C5/LinkedLists/LinkedList.cs b/C5/LinkedLists/LinkedList.cs index f9f86221..03a7f381 100644 --- a/C5/LinkedLists/LinkedList.cs +++ b/C5/LinkedLists/LinkedList.cs @@ -776,6 +776,8 @@ internal Range(LinkedList list, int start, int count, bool forwards) } } + public override bool IsReadOnly => true; + public override bool IsEmpty { get { list.ModifyCheck(rangestamp); return count == 0; } } public override int Count { get { list.ModifyCheck(rangestamp); return count; } } diff --git a/C5/Trees/TreeBag.cs b/C5/Trees/TreeBag.cs index 026d96e2..1df0d9ac 100644 --- a/C5/Trees/TreeBag.cs +++ b/C5/Trees/TreeBag.cs @@ -2310,6 +2310,7 @@ private class Multiplicities : CollectionValueBase treebag.IsReadOnly; public override bool IsEmpty => treebag.IsEmpty; public override int Count { get { int i = 0; foreach (System.Collections.Generic.KeyValuePair p in this) { i++; } return i; } } //TODO: make better public override Speed CountSpeed => Speed.Linear; //TODO: make better @@ -2701,6 +2702,8 @@ internal Interval(TreeBag tree, int start, int count, bool forwards) this.tree = tree; stamp = tree.stamp; } + public override bool IsReadOnly => true; + public override bool IsEmpty => length == 0; public override int Count => length; @@ -4288,6 +4291,7 @@ public override IDirectedCollectionValue Backwards() IDirectedEnumerable IDirectedEnumerable.Backwards() { return Backwards(); } + public override bool IsReadOnly => true; public override bool IsEmpty => size == 0; diff --git a/C5/Trees/TreeSet.cs b/C5/Trees/TreeSet.cs index f58f8250..6e265d0f 100644 --- a/C5/Trees/TreeSet.cs +++ b/C5/Trees/TreeSet.cs @@ -21,7 +21,7 @@ namespace C5 /// leak possible with other usage modes. /// [Serializable] - public class TreeSet : SequencedBase, IIndexedSorted, IPersistentSorted + public class TreeSet : SequencedBase, IIndexedSorted, IPersistentSorted, SCG.ISet { #region Fields @@ -522,6 +522,479 @@ void System.Collections.IEnumerator.Reset() #endregion + #region ISet Members + + /// + /// Modifies the current object to contain all elements that are present in itself, the specified collection, or both. + /// + /// The collection to compare to the current set. + /// If is null. + public virtual void UnionWith(SCG.IEnumerable other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + AddAll(other); + } + + /// + /// Modifies the current object so that it contains only elements that are also in a specified collection. + /// + /// The collection to compare to the current set. + /// If is null. + public virtual void IntersectWith(SCG.IEnumerable other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + // intersection of anything with empty set is empty set, so return if count is 0 + if (this.size == 0) + { + return; + } + + // if other is empty, intersection is empty set; remove all elements and we're done + // can only figure this out if implements ICollection. (IEnumerable has no count) + if (other is SCG.ICollection otherAsCollection) + { + if (otherAsCollection.Count == 0) + { + Clear(); + return; + } + + // faster if other is a hashset using same equality comparer; so check + // that other is a hashset using the same equality comparer. + if (AreEqualityComparersEqual(this, otherAsCollection)) + { + IntersectWithHashSetWithSameEC(otherAsCollection); + return; + } + } + + IntersectWithEnumerable(other); + } + + /// + /// Removes all elements in the specified collection from the current object. + /// + /// The collection of items to remove from the set. + /// If is null. + public virtual void ExceptWith(SCG.IEnumerable other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + // this is already the enpty set; return + if (this.size == 0) + return; + + // special case if other is this; a set minus itself is the empty set + if (other == this) + { + Clear(); + return; + } + + // remove every element in other from this + foreach (T element in other) + { + Remove(element); + } + } + + /// + /// Modifies the current set so that it contains only elements that are present either in the current + /// object or in the specified collection, but not both. + /// + /// The collection to compare to the current object. + /// If is null. + public virtual void SymmetricExceptWith(SCG.IEnumerable other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + // if set is empty, then symmetric difference is other + if (this.size == 0) + { + UnionWith(other); + return; + } + + // special case this; the symmetric difference of a set with itself is the empty set + if (other == this) + { + Clear(); + return; + } + + // If other is a HashSet, it has unique elements according to its equality comparer, + // but if they're using different equality comparers, then assumption of uniqueness + // will fail. So first check if other is a hashset using the same equality comparer; + // symmetric except is a lot faster and avoids bit array allocations if we can assume + // uniqueness + if (other is SCG.ICollection otherAsCollection && AreEqualityComparersEqual(this, otherAsCollection)) + { + SymmetricExceptWithUniqueTreeSet(otherAsCollection); + } + else + { + var temp = new SCG.SortedSet(other, comparer); + temp.ExceptWith(this); + ExceptWith(other); + UnionWith(temp); + } + } + + /// + /// Determines whether a object is a subset of the specified collection. + /// + /// The collection to compare to the current object. + /// true if the object is a subset of other; otherwise, false. + /// If is null. + public virtual bool IsSubsetOf(SCG.IEnumerable other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + if (this.size == 0) + { + return true; + } + + // faster if other has unique elements according to this equality comparer; so check + // that other is a hashset using the same equality comparer. + if (other is SCG.ICollection otherAsCollection && AreEqualityComparersEqual(this, otherAsCollection)) + { + // if this has more elements then it can't be a subset + if (this.size > otherAsCollection.Count) + { + return false; + } + + // already checked that we're using same equality comparer. simply check that + // each element in this is contained in other. + return IsSubsetOfTreeSetWithSameEC(otherAsCollection); + } + + // we just need to return true if the other set + // contains all of the elements of the this set, + // but we need to use the comparison rules of the current set. + this.CheckUniqueAndUnfoundElements(other, false, out int uniqueCount, out int _); + return uniqueCount == this.size; + } + + /// + /// Determines whether a object is a superset of the specified collection. + /// + /// The collection to compare to the current object. + /// true if the object is a superset of other; otherwise, false. + /// If is null. + public virtual bool IsSupersetOf(SCG.IEnumerable other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + // try to fall out early based on counts + if (other is SCG.ICollection otherAsCollection) + { + // if other is the empty set then this is a superset + if (otherAsCollection.Count == 0) + return true; + + // try to compare based on counts alone if other is a hashset with + // same equality comparer + if (AreEqualityComparersEqual(this, otherAsCollection)) + { + if (otherAsCollection.Count > this.size) + { + return false; + } + } + } + + return this.ContainsAll(other); + } + + /// + /// Determines whether a object is a proper superset of the specified collection. + /// + /// The collection to compare to the current object. + /// true if the object is a proper superset of other; otherwise, false. + /// If is null. + public virtual bool IsProperSupersetOf(SCG.IEnumerable other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + // the empty set isn't a proper superset of any set. + if (this.size == 0) + { + return false; + } + + if (other is SCG.ICollection otherAsCollection) + { + // if other is the empty set then this is a superset + if (otherAsCollection.Count == 0) + return true; // note that this has at least one element, based on above check + + // faster if other is a hashset with the same equality comparer + if (AreEqualityComparersEqual(this, otherAsCollection)) + { + if (otherAsCollection.Count >= this.size) + { + return false; + } + // now perform element check + return ContainsAll(otherAsCollection); + } + } + + // couldn't fall out in the above cases; do it the long way + this.CheckUniqueAndUnfoundElements(other, true, out int uniqueCount, out int unfoundCount); + return uniqueCount < this.size && unfoundCount == 0; + } + + /// + /// Determines whether a object is a proper subset of the specified collection. + /// + /// The collection to compare to the current object. + /// true if the object is a proper subset of other; otherwise, false. + /// If is null. + public virtual bool IsProperSubsetOf(SCG.IEnumerable other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + + if (other is SCG.ICollection otherAsCollection) + { + // the empty set is a proper subset of anything but the empty set + if (this.size == 0) + return otherAsCollection.Count > 0; + + // faster if other is a hashset (and we're using same equality comparer) + if (AreEqualityComparersEqual(this, otherAsCollection)) + { + if (this.size >= otherAsCollection.Count) + { + return false; + } + // this has strictly less than number of items in other, so the following + // check suffices for proper subset. + return IsSubsetOfTreeSetWithSameEC(otherAsCollection); + } + } + + this.CheckUniqueAndUnfoundElements(other, false, out int uniqueCount, out int unfoundCount); + return uniqueCount == this.size && unfoundCount > 0; + } + + /// + /// Determines whether the current object and a specified collection share common elements. + /// + /// The collection to compare to the current object. + /// true if the object and other share at least one common element; otherwise, false. + /// If is null. + public virtual bool Overlaps(SCG.IEnumerable other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + if (this.size != 0) + { + foreach (var local in other) + { + if (this.Contains(local)) + { + return true; + } + } + } + return false; + } + + /// + /// Determines whether the current and the specified collection contain the same elements. + /// + /// The collection to compare to the current . + /// true if the current is equal to other; otherwise, false. + /// If is null. + public virtual bool SetEquals(SCG.IEnumerable other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + // faster if other is a treeset and we're using same equality comparer + if (other is SCG.ICollection otherAsCollection) + { + if (AreEqualityComparersEqual(this, otherAsCollection)) + { + // attempt to return early: since both contain unique elements, if they have + // different counts, then they can't be equal + if (this.size != otherAsCollection.Count) + return false; + + // already confirmed that the sets have the same number of distinct elements, so if + // one is a superset of the other then they must be equal + return ContainsAll(otherAsCollection); + } + else + { + // if this count is 0 but other contains at least one element, they can't be equal + if (this.size == 0 && otherAsCollection.Count > 0) + return false; + } + } + + this.CheckUniqueAndUnfoundElements(other, true, out int uniqueCount, out int unfoundCount); + return uniqueCount == this.size && unfoundCount == 0; + } + + private void CheckUniqueAndUnfoundElements(SCG.IEnumerable other, bool returnIfUnfound, out int uniqueCount, out int unfoundCount) + { + // need special case in case this has no elements. + if (this.size == 0) + { + int numElementsInOther = 0; + foreach (T item in other) + { + numElementsInOther++; + // break right away, all we want to know is whether other has 0 or 1 elements + break; + } + uniqueCount = 0; + unfoundCount = numElementsInOther; + return; + } + + int originalLastIndex = this.size; + var bitArray = new System.Collections.BitArray(originalLastIndex, false); + + // count of unique items in other found in this + uniqueCount = 0; + // count of items in other not found in this + unfoundCount = 0; + + foreach (var item in other) + { + var index = IndexOf(item); + if (index >= 0) + { + if (!bitArray.Get(index)) + { + // item hasn't been seen yet + bitArray.Set(index, true); + uniqueCount++; + } + } + else + { + unfoundCount++; + if (returnIfUnfound) + break; + } + } + } + + /// + /// Checks if equality comparers are equal. This is used for algorithms that can + /// speed up if it knows the other item has unique elements. I.e. if they're using + /// different equality comparers, then uniqueness assumption between sets break. + /// + private static bool AreEqualityComparersEqual(TreeSet set1, SCG.ICollection set2) + { + if (set2 is TreeSet treeSet) + return set1.EqualityComparer.Equals(treeSet.EqualityComparer); + else if (set2 is HashSet hashSet) + return set1.EqualityComparer.Equals(hashSet.EqualityComparer); + else if (set2 is SCG.HashSet scgHashSet) + return set1.EqualityComparer.Equals(scgHashSet.Comparer); + return false; + } + + /// + /// If other is a hashset that uses same equality comparer, intersect is much faster + /// because we can use other's Contains + /// + private void IntersectWithHashSetWithSameEC(SCG.ICollection other) + { + foreach (var item in this) + { + if (!other.Contains(item)) + { + Remove(item); + } + } + } + + private void IntersectWithEnumerable(SCG.IEnumerable other) + { + // keep track of current last index; don't want to move past the end of our bit array + // (could happen if another thread is modifying the collection) + int originalLastIndex = this.size; + var bitArray = new System.Collections.BitArray(originalLastIndex, false); + + foreach (var item in other) + { + int index = IndexOf(item); + if (index >= 0) + bitArray.Set(index, true); + } + + // if anything unmarked, remove it. + for (int i = originalLastIndex - 1; i >= 0; i--) + { + if (!bitArray.Get(i)) + RemoveAt(i); + } + } + + /// + /// if other is a set, we can assume it doesn't have duplicate elements, so use this + /// technique: if can't remove, then it wasn't present in this set, so add. + /// + /// As with other methods, callers take care of ensuring that other is a hashset using the + /// same equality comparer. + /// + private void SymmetricExceptWithUniqueTreeSet(SCG.ICollection other) + { + foreach (T item in other) + { + if (!Remove(item)) + { + Add(item); + } + } + } + + /// + /// Implementation Notes: + /// If other is a hashset and is using same equality comparer, then checking subset is + /// faster. Simply check that each element in this is in other. + /// + /// Note: if other doesn't use same equality comparer, then Contains check is invalid, + /// which is why callers must take are of this. + /// + /// If callers are concerned about whether this is a proper subset, they take care of that. + /// + /// + private bool IsSubsetOfTreeSetWithSameEC(SCG.ICollection other) + { + + foreach (T item in this) + { + if (!other.Contains(item)) + { + return false; + } + } + return true; + } + + #endregion + #region IEnumerable Members private SCG.IEnumerator GetEnumerator(Node? node, int origstamp) @@ -2435,6 +2908,8 @@ internal Interval(TreeSet tree, int start, int count, bool forwards) this.tree = tree; stamp = tree.stamp; } + public override bool IsReadOnly => true; + public override bool IsEmpty => length == 0; public override int Count => length; @@ -3945,6 +4420,7 @@ public override IDirectedCollectionValue Backwards() IDirectedEnumerable IDirectedEnumerable.Backwards() { return Backwards(); } + public override bool IsReadOnly => true; public override bool IsEmpty => size == 0; diff --git a/C5/WeakViewList.cs b/C5/WeakViewList.cs index 5d64b0d2..11f5fe9c 100644 --- a/C5/WeakViewList.cs +++ b/C5/WeakViewList.cs @@ -58,7 +58,7 @@ public SCG.IEnumerator GetEnumerator() { //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; + V? view = o is V ? (V)o : null; if (view == null) { Remove(n); diff --git a/C5/Wrappers/GuardedCollection.cs b/C5/Wrappers/GuardedCollection.cs index 947769ba..c9b24606 100644 --- a/C5/Wrappers/GuardedCollection.cs +++ b/C5/Wrappers/GuardedCollection.cs @@ -37,7 +37,7 @@ public GuardedCollection(ICollection collection) /// (This is a read-only wrapper) /// /// True - public virtual bool IsReadOnly => true; + public override bool IsReadOnly => true; /// @@ -65,7 +65,7 @@ public virtual bool UnsequencedEquals(ICollection that) /// /// The item /// True if found - public virtual bool Contains(T item) { return collection.Contains(item); } + public override bool Contains(T item) => collection.Contains(item); /// @@ -73,7 +73,7 @@ public virtual bool UnsequencedEquals(ICollection that) /// /// The item /// The number of copies - public virtual int ContainsCount(T item) { return collection.ContainsCount(item); } + public virtual int ContainsCount(T item) => collection.ContainsCount(item); /// /// @@ -153,7 +153,7 @@ public virtual bool UpdateOrAdd(T item, out T olditem) /// since this is a read-only wrapper /// /// - public virtual bool Remove(T item) + public override bool Remove(T item) { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); } @@ -185,7 +185,7 @@ public virtual void RemoveAll(System.Collections.Generic.IEnumerable items) /// /// /// since this is a read-only wrapper - public virtual void Clear() + public override void Clear() { throw new ReadOnlyCollectionException("Collection cannot be modified through this guard object"); } @@ -235,7 +235,7 @@ public virtual void RetainAll(System.Collections.Generic.IEnumerable items) /// since this is a read-only wrapper /// /// - public virtual bool Add(T item) + public override bool Add(T item) { throw new ReadOnlyCollectionException(); } /// diff --git a/C5/Wrappers/GuardedCollectionValue.cs b/C5/Wrappers/GuardedCollectionValue.cs index 62293403..6238a2f7 100644 --- a/C5/Wrappers/GuardedCollectionValue.cs +++ b/C5/Wrappers/GuardedCollectionValue.cs @@ -219,6 +219,43 @@ public GuardedCollectionValue(ICollectionValue collectionvalue) #endregion + #region SCG.ICollection Members + + /// + /// Gets a value indicating whether the is read-only. + /// + public virtual bool IsReadOnly => collectionvalue.IsReadOnly; + + /// + /// Adds an item to the . + /// + /// The object to add to the . + public virtual bool Add(T item) => collectionvalue.Add(item); + + void System.Collections.Generic.ICollection.Add(T item) => this.Add(item); + + /// + /// Removes all items from the . + /// + public virtual void Clear() => collectionvalue.Clear(); + + /// + /// Determines whether the contains a specific value. + /// + /// The object to locate in the . + /// true if item is found in the ; otherwise, false. + public virtual bool Contains(T item) => collectionvalue.Contains(item); + + /// + /// Removes the first occurrence of a specific object from the . + /// + /// The object to remove from the . + /// true if item was successfully removed from the ; otherwise, false. + /// This method also returns false if item is not found in the original . + public virtual bool Remove(T item) => collectionvalue.Remove(item); + + #endregion + #region IShowable Members /// diff --git a/C5/Wrappers/GuardedDictionary.cs b/C5/Wrappers/GuardedDictionary.cs index 248325d0..a073e94f 100644 --- a/C5/Wrappers/GuardedDictionary.cs +++ b/C5/Wrappers/GuardedDictionary.cs @@ -50,7 +50,7 @@ public V this[K key] /// (This is a read-only wrapper) /// /// True - public bool IsReadOnly => true; + public override bool IsReadOnly => true; /// /// The collection of keys of the wrapped dictionary @@ -100,7 +100,7 @@ public void AddAll(SCG.IEnumerable> items) /// /// /// since this is a read-only wrapper - public void Clear() => throw new ReadOnlyCollectionException(); + public override void Clear() => throw new ReadOnlyCollectionException(); /// ///