Skip to content
Open
Show file tree
Hide file tree
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,10 @@ 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();
}

return IsProperSubsetOf(other, this.Origin);
}
Expand Down Expand Up @@ -925,52 +929,55 @@ private static MutationResult SymmetricExcept(IEnumerable<T> other, MutationInpu
return new MutationResult(result, count, CountType.FinalValue);
}

/// <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));

if (origin.Root.IsEmpty)
// For a proper subset, the 'other' collection must strictly exceed 'origin.Count'.
// We use '<=' as an early exit to reject smaller or equal-sized collections.
// This is safe even for non-unique collections (ICollection<T>): if the raw count
// is already insufficient, it cannot become a proper superset after deduplication.
switch (other)
{
return other.Any();
case ImmutableHashSet<T> otherAsImmutableHashSet:
if (otherAsImmutableHashSet.Count <= origin.Count)
Comment thread
aw0lid marked this conversation as resolved.
{
return false;
}

if (EqualityComparer<IEqualityComparer<T>>.Default.Equals(origin.EqualityComparer, otherAsImmutableHashSet.KeyComparer))
Comment thread
aw0lid marked this conversation as resolved.
{
return SetEqualsWithImmutableHashset(otherAsImmutableHashSet, origin);
}
break;

case HashSet<T> otherAsHashset:
if (otherAsHashset.Count <= origin.Count)
{
return false;
}

if (EqualityComparer<IEqualityComparer<T>>.Default.Equals(origin.EqualityComparer, otherAsHashset.Comparer))
{
return SetEqualsWithHashset(otherAsHashset, origin);
}
break;

case ICollection<T> otherAsICollectionGeneric:
if (otherAsICollectionGeneric.Count <= origin.Count)
{
return false;
}
break;
}

// 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 (otherSet.Count <= origin.Count)
Comment thread
aw0lid marked this conversation as resolved.
{
return false;
}

int matches = 0;
bool extraFound = false;
foreach (T item in otherSet)
{
if (Contains(item, origin))
{
matches++;
}
else
{
extraFound = true;
}

if (matches == origin.Count && extraFound)
{
return true;
}
}

return false;
return SetEqualsWithHashset(otherSet, origin);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,61 @@ public void SetEqualsMismatchedComparersOtherIsLarger()
Assert.False(origin.SetEquals(other));
}

[Fact]
public void IsProperSubsetOfMismatchedComparersLargerCountMissingElement()
{
var origin = ImmutableHashSet.Create(StringComparer.Ordinal, "a", "A");
var other = ImmutableHashSet.Create(StringComparer.OrdinalIgnoreCase, "a", "b", "c", "d");

Assert.False(origin.IsProperSubsetOf(other));
}

[Fact]
public void IsProperSubsetOfICollectionLargeCountSmallUniqueSet()
{
var origin = ImmutableHashSet.Create(StringComparer.Ordinal, "a", "b");
var other = new List<string> { "a", "b", "b", "b" };

Assert.False(origin.IsProperSubsetOf(other));
}

[Fact]
public void IsProperSubsetOfEmptyAgainstAnything()
{
var empty = ImmutableHashSet<string>.Empty;
var other = ImmutableHashSet.Create("any");

Assert.True(empty.IsProperSubsetOf(other));
Assert.False(empty.IsProperSubsetOf(new List<string>()));
}

[Fact]
public void IsProperSubsetOfSensitiveOriginInsensitiveOtherTrueCase()
{
var origin = ImmutableHashSet.Create(StringComparer.Ordinal, "a");
var other = ImmutableHashSet.Create(StringComparer.OrdinalIgnoreCase, "A", "b");

Assert.False(origin.IsProperSubsetOf(other));
}

[Fact]
public void IsProperSubsetOfMismatchedLogicallyEqual()
{
var origin = ImmutableHashSet.Create(StringComparer.OrdinalIgnoreCase, "a");
var other = ImmutableHashSet.Create(StringComparer.Ordinal, "a", "A");

Assert.False(origin.IsProperSubsetOf(other));
}

[Fact]
public void IsProperSubsetOfInsensitiveOriginSensitiveOther()
{
var origin = ImmutableHashSet.Create(StringComparer.OrdinalIgnoreCase, "a", "b");
var other = ImmutableHashSet.Create(StringComparer.Ordinal, "A", "B", "c");

Assert.True(origin.IsProperSubsetOf(other));
}

[Fact]
public void ChangeUnorderedEqualityComparer()
{
Expand Down
Loading