Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,20 @@ public bool SetEquals(IEnumerable<T> other)
public bool IsProperSubsetOf(IEnumerable<T> other)
{
Requires.NotNull(other, nameof(other));
if (this.Origin.Root.IsEmpty)
{
return other.Any();
}

if (other is ICollection<T> otherAsICollectionGeneric)
{
return IsProperSubsetOfFastPath(otherAsICollectionGeneric, this.Origin);
}

else if (other is ICollection otherAsICollection && otherAsICollection.Count <= this.Origin.Count)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it common for types to implement ICollection but not ICollection<T> if they're already implementing IEnumerable<T>?

{
return false;
}

return IsProperSubsetOf(other, this.Origin);
}
Expand Down Expand Up @@ -863,49 +877,78 @@ private static MutationResult SymmetricExcept(IEnumerable<T> other, MutationInpu
/// <summary>
/// Performs the set operation on a given data structure.
/// </summary>
private static bool IsProperSubsetOf(IEnumerable<T> other, MutationInput origin)
private static bool IsProperSubsetOfFastPath(ICollection<T> other, MutationInput origin)
{
Requires.NotNull(other, nameof(other));

if (origin.Root.IsEmpty)
{
return other.Any();
}

// To determine whether everything we have is also in another sequence,
// we enumerate the sequence and "tag" whether it's in this collection,
// then consider whether every element in this collection was tagged.
// Since this collection is immutable we cannot directly tag. So instead
// we simply count how many "hits" we have and ensure it's equal to the
// size of this collection. Of course for this to work we need to ensure
// the uniqueness of items in the given sequence, so we create a set based
// on the sequence first.
var otherSet = new HashSet<T>(other, origin.EqualityComparer);
if (origin.Count >= otherSet.Count)
if (other.Count <= origin.Count)
{
return false;
}

int matches = 0;
bool extraFound = false;
foreach (T item in otherSet)
if (other is ImmutableHashSet<T> otherAsImmutableHashSet)
{
if (Contains(item, origin))
{
matches++;
}
else
if (otherAsImmutableHashSet.KeyComparer == origin.EqualityComparer)
{
extraFound = true;
using (var e = new ImmutableHashSet<T>.Enumerator(origin.Root))
{
while (e.MoveNext())
{
if (!otherAsImmutableHashSet.Contains(e.Current))
{
return false;
}
}
}
Comment on lines +893 to +902
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
using (var e = new ImmutableHashSet<T>.Enumerator(origin.Root))
{
while (e.MoveNext())
{
if (!otherAsImmutableHashSet.Contains(e.Current))
{
return false;
}
}
}
foreach (T value in origin)
{
if (!otherAsImmutableHashSet.Contains(value))
{
return false;
}
}

Any reason why all of the loops are manually deconstructed instead of using foreach?

return true;
}
}

if (matches == origin.Count && extraFound)
else if (other is HashSet<T> otherAsHashSet)
{
if (otherAsHashSet.Comparer == origin.EqualityComparer)
{
using (var e = new ImmutableHashSet<T>.Enumerator(origin.Root))
{
while (e.MoveNext())
{
if (!otherAsHashSet.Contains(e.Current))
{
return false;
}
}
}
return true;
}
}

return false;
return IsProperSubsetOf(other, origin);
}

/// <summary>
/// Performs the set operation on a given data structure.
/// </summary>
private static bool IsProperSubsetOf(IEnumerable<T> other, MutationInput origin)
{
Requires.NotNull(other, nameof(other));

var otherSet = new HashSet<T>(other, origin.EqualityComparer);
if (otherSet.Count <= origin.Count)
{
return false;
}

using (var e = new ImmutableHashSet<T>.Enumerator(origin.Root))
{
while (e.MoveNext())
{
if (!otherSet.Contains(e.Current))
{
return false;
}
}
}
return true;
}

/// <summary>
Expand Down
Loading