diff --git a/README.md b/README.md index 6f0c29298..c7994660f 100644 --- a/README.md +++ b/README.md @@ -1868,6 +1868,7 @@ $bool = $A->isProperSuperset($B); // A ⊇ B & A ≠ B // Set operations with other sets - return a new Set $A∪B = $A->union($B); $A∩B = $A->intersect($B); +$A∩B = $A->intersectPartial(2, $B); // M-partial intersection $A\B = $A->difference($B); // relative complement $AΔB = $A->symmetricDifference($B); $A×B = $A->cartesianProduct($B); diff --git a/src/SetTheory/Set.php b/src/SetTheory/Set.php index fcb7720b4..24c5a5322 100644 --- a/src/SetTheory/Set.php +++ b/src/SetTheory/Set.php @@ -2,6 +2,9 @@ namespace MathPHP\SetTheory; +use MathPHP\Util\JustifyMultipleIterator; +use MathPHP\Util\NoValueMonad; + /** * Set (Set Theory) * A set is a collection of distinct objects, considered as an object in @@ -374,6 +377,7 @@ public function isProperSuperset(Set $B): bool * SET OPERATIONS ON OTHER SETS * - Union * - Intersection + * - Partial intersection * - Difference * - Symmetric difference **************************************************************************/ @@ -428,6 +432,63 @@ public function intersect(Set ...$Bs): Set return new Set($intersection); } + /** + * Produces a new set of the M-partial intersection of this set and another given sets. + * + * Definition: + * + * An M-partial intersection (for M > 0) of N sets is a set elements in which + * are contained in at least M initial sets. + * + * Properties: + * + * - 1-partial intersection is equivalent to the union of these sets. + * - 2-partial intersection is equivalent to the difference of the union and the symmetric difference of these sets. + * - N-partial intersection is equivalent to the common (complete) intersection of these sets. + * - For any M > N M-partial intersection always equals to the empty set. + * + * @see https://github.com/Smoren/partial-intersection-php for the explanation and the examples. + * + * @param int $m Min intersection count + * @param Set ...$Bs One or more sets + * + * @return Set + */ + public function intersectPartial(int $m, Set ...$Bs): Set + { + $B_members = []; + foreach ($Bs as $B) { + $B_members[] = $B->asArray(); + } + + $iterator = new JustifyMultipleIterator($this->asArray(), ...$B_members); + + $usageMap = []; + $intersection = []; + + foreach ($iterator as $values) { + foreach ($values as $value) { + if ($value instanceof NoValueMonad) { + continue; + } + + $key = $this->getKey($value); + + if (!isset($usageMap[$key])) { + $usageMap[$key] = 0; + } + + $usageMap[$key]++; + + if ($usageMap[$key] === $m) { + $intersection[] = $value; + } + } + } + + return new Set($intersection); + } + /** * Difference (relative complement) (A ∖ B) or (A - B) * Produces a new set with elements that are not in the other sets. diff --git a/src/Util/Iter.php b/src/Util/Iter.php index 1ccba22a9..2d1e1dee2 100644 --- a/src/Util/Iter.php +++ b/src/Util/Iter.php @@ -33,7 +33,7 @@ public static function zip(iterable ...$iterables): \MultipleIterator * * @return \Iterator|\IteratorIterator|\ArrayIterator */ - private static function makeIterator(iterable $iterable): \Iterator + public static function makeIterator(iterable $iterable): \Iterator { switch (true) { case $iterable instanceof \Iterator: diff --git a/src/Util/JustifyMultipleIterator.php b/src/Util/JustifyMultipleIterator.php new file mode 100644 index 000000000..013cde188 --- /dev/null +++ b/src/Util/JustifyMultipleIterator.php @@ -0,0 +1,95 @@ +> + * + * Based on IterTools PHP's IteratorFactory. + * @see https://github.com/markrogoyski/itertools-php + * @see https://github.com/markrogoyski/itertools-php/blob/main/src/Util/JustifyMultipleIterator.php + */ +class JustifyMultipleIterator implements \Iterator +{ + /** + * @var array<\Iterator> + */ + protected $iterators = []; + /** + * @var int + */ + protected $index = 0; + + /** + * @param iterable ...$iterables + */ + public function __construct(iterable ...$iterables) + { + foreach ($iterables as $iterable) { + $this->iterators[] = Iter::makeIterator($iterable); + } + } + + /** + * {@inheritDoc} + * + * @return array + */ + public function current(): array + { + return array_map( + static function (\Iterator $iterator) { + return $iterator->valid() ? $iterator->current() : NoValueMonad::getInstance(); + }, + $this->iterators + ); + } + + /** + * {@inheritDoc} + */ + public function next(): void + { + foreach ($this->iterators as $iterator) { + if ($iterator->valid()) { + $iterator->next(); + } + } + $this->index++; + } + + /** + * {@inheritDoc} + * + * @return int + */ + public function key(): int + { + return $this->index; + } + + /** + * {@inheritDoc} + */ + public function valid(): bool + { + foreach ($this->iterators as $iterator) { + if ($iterator->valid()) { + return true; + } + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function rewind(): void + { + foreach ($this->iterators as $iterator) { + $iterator->rewind(); + } + $this->index = 0; + } +} diff --git a/src/Util/NoValueMonad.php b/src/Util/NoValueMonad.php new file mode 100644 index 000000000..86e1ee0e9 --- /dev/null +++ b/src/Util/NoValueMonad.php @@ -0,0 +1,29 @@ +assertEquals(\pow(2, $n), count($P⟮S⟯)); $this->assertEquals(\pow(2, $n), count($P⟮S⟯->asArray())); } + + /** + * An M-partial intersection (for M > 0) of N sets is a set elements in which + * are contained in at least M initial sets. + * + * @test + * @dataProvider dataProviderForPartialIntersectionDefinition + * @param int $m + * @param Set ...$sets + * @return void + */ + public function testPartialIntersectionDefinition(int $m, Set ...$sets) + { + // Given + $allSets = $sets; + $s = array_shift($sets); + $totalElementsCount = array_reduce($allSets, static function (int $carry, Set $set) { + return $carry + count($set); + }, 0); + $partialIntersection = $s->intersectPartial($m, ...$sets); + + // When + if ($totalElementsCount === 0) { + // Then + // Assert than for any M and N M-partial intersection of N empty sets is an empty set. + $this->assertCount(0, $partialIntersection); + } + + // Then + foreach ($partialIntersection as $value) { + $usageCount = array_reduce($allSets, static function (int $carry, Set $currentSet) use ($value) { + return $carry + intval($currentSet->isMember($value)); + }, 0); + + // Assert that every element in M-partial intersection occurs in at least M sets. + $this->assertTrue($usageCount >= $m); + } + + // Then + foreach ($allSets as $set) { + $notInPartialIntersection = $set->difference($partialIntersection); + foreach ($notInPartialIntersection as $value) { + $usageCount = array_reduce($allSets, static function (int $carry, Set $currentSet) use ($value) { + return $carry + intval($currentSet->isMember($value)); + }, 0); + + // Assert that elements from sets and not in M-partial intersection occurs in less than M sets. + $this->assertTrue($usageCount < $m); + } + } + } + + public function dataProviderForPartialIntersectionDefinition(): array + { + return [ + [ + 1, + new Set(), + ], + [ + 2, + new Set(), + ], + [ + 1, + new Set([1]), + ], + [ + 2, + new Set([1]), + ], + [ + 1, + new Set(), + new Set(), + ], + [ + 2, + new Set(), + new Set(), + ], + [ + 3, + new Set(), + new Set(), + ], + [ + 1, + new Set([1]), + new Set(), + ], + [ + 2, + new Set([1]), + new Set(), + ], + [ + 1, + new Set(), + new Set([1]), + ], + [ + 2, + new Set(), + new Set([1]), + ], + [ + 1, + new Set([1]), + new Set([1]), + ], + [ + 2, + new Set([1]), + new Set([1]), + ], + [ + 3, + new Set([1]), + new Set([1]), + ], + [ + 1, + new Set([1, 2, 3, 4, 5]), + new Set([2, 3, 4, 5, 6]), + ], + [ + 2, + new Set([1, 2, 3, 4, 5]), + new Set([2, 3, 4, 5, 6]), + ], + [ + 3, + new Set([1, 2, 3, 4, 5]), + new Set([2, 3, 4, 5, 6]), + ], + [ + 1, + new Set([1, 2, 3, 4, 5]), + new Set([6, 7, 8, 9, 10]), + ], + [ + 2, + new Set([1, 2, 3, 4, 5]), + new Set([6, 7, 8, 9, 10]), + ], + [ + 3, + new Set([1, 2, 3, 4, 5]), + new Set([6, 7, 8, 9, 10]), + ], + [ + 1, + new Set(), + new Set(), + new Set(), + ], + [ + 2, + new Set(), + new Set(), + new Set(), + ], + [ + 3, + new Set(), + new Set(), + new Set(), + ], + [ + 4, + new Set(), + new Set(), + new Set(), + ], + [ + 1, + new Set([1, 2, 3, 4, 5]), + new Set([2, 3, 4, 5, 6]), + new Set([3, 4, 5, 6, 7]), + ], + [ + 2, + new Set([1, 2, 3, 4, 5]), + new Set([2, 3, 4, 5, 6]), + new Set([3, 4, 5, 6, 7]), + ], + [ + 3, + new Set([1, 2, 3, 4, 5]), + new Set([2, 3, 4, 5, 6]), + new Set([3, 4, 5, 6, 7]), + ], + [ + 4, + new Set([1, 2, 3, 4, 5]), + new Set([2, 3, 4, 5, 6]), + new Set([3, 4, 5, 6, 7]), + ], + [ + 1, + new Set([1, 2, 3, 4, 5]), + new Set([6, 7, 8, 9, 10]), + new Set([11, 12, 13, 14, 15]), + ], + [ + 2, + new Set([1, 2, 3, 4, 5]), + new Set([6, 7, 8, 9, 10]), + new Set([11, 12, 13, 14, 15]), + ], + [ + 3, + new Set([1, 2, 3, 4, 5]), + new Set([6, 7, 8, 9, 10]), + new Set([11, 12, 13, 14, 15]), + ], + [ + 4, + new Set([1, 2, 3, 4, 5]), + new Set([6, 7, 8, 9, 10]), + new Set([11, 12, 13, 14, 15]), + ], + [ + 1, + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + ], + [ + 2, + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + ], + [ + 3, + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + ], + [ + 4, + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + ], + [ + 1, + new Set(), + new Set(), + new Set(), + new Set(), + ], + [ + 2, + new Set(), + new Set(), + new Set(), + new Set(), + ], + [ + 3, + new Set(), + new Set(), + new Set(), + new Set(), + ], + [ + 4, + new Set(), + new Set(), + new Set(), + new Set(), + ], + [ + 5, + new Set(), + new Set(), + new Set(), + new Set(), + ], + [ + 1, + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + new Set(), + ], + [ + 2, + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + new Set(), + ], + [ + 3, + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + new Set(), + ], + [ + 4, + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + new Set(), + ], + [ + 5, + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + new Set(), + ], + [ + 1, + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + new Set([3, 4, 5, 6, 7, 8]), + ], + [ + 2, + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + new Set([3, 4, 5, 6, 7, 8]), + ], + [ + 3, + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + new Set([3, 4, 5, 6, 7, 8]), + ], + [ + 4, + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + new Set([3, 4, 5, 6, 7, 8]), + ], + [ + 5, + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + new Set([3, 4, 5, 6, 7, 8]), + ], + ]; + } + + /** + * 1-partial intersection is equivalent to the union of these sets. + * + * @test + * @dataProvider dataProviderForOnePartialIntersectionIsUnion + * @param Set $s + * @param Set ...$others + * @return void + */ + public function testOnePartialIntersectionIsUnion(Set $s, Set ...$others) + { + // Given + $onePartialIntersection = $s->intersectPartial(1, ...$others); + + // When + $union = $s->union(...$others); + + // Then + $this->assertEquals($union, $onePartialIntersection); + } + + public function dataProviderForOnePartialIntersectionIsUnion(): array + { + return [ + [ + new Set(), + new Set(), + ], + [ + new Set(), + new Set(), + new Set(), + ], + [ + new Set([1]), + new Set(), + ], + [ + new Set(), + new Set([1]), + ], + [ + new Set([1]), + new Set([1]), + ], + [ + new Set([1, 2, 3, 4, 5]), + new Set([2, 3, 4, 5, 6]), + ], + [ + new Set([1, 2, 3, 4, 5]), + new Set([6, 7, 8, 9, 10]), + ], + [ + new Set([1, 2, 3, 4, 5]), + new Set([2, 3, 4, 5, 6]), + new Set([3, 4, 5, 6, 7]), + ], + [ + new Set([1, 2, 3, 4, 5]), + new Set([6, 7, 8, 9, 10]), + new Set([11, 12, 13, 14, 15]), + ], + [ + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + ], + [ + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + new Set(), + ], + [ + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + new Set([3, 4, 5, 6, 7, 8]), + ], + ]; + } + + /** + * 2-partial intersection is equivalent to the difference of the union and the symmetric difference of these sets. + * + * @test + * @dataProvider dataProviderForTwoPartialIntersectionIsDifferenceOfUnionAndSymmetricDifference + * @param Set $lhs + * @param Set $rhs + */ + public function testTwoPartialIntersectionIsDifferenceOfUnionAndSymmetricDifference(Set $lhs, Set $rhs) + { + // Given + $onePartialIntersection = $lhs->intersectPartial(2, $rhs); + + // When + $union = $lhs->union($rhs); + $symmetricDifference = $lhs->symmetricDifference($rhs); + $diffOfUnionAndSymmetricDifference = $union->difference($symmetricDifference); + + // Then + $this->assertEquals($diffOfUnionAndSymmetricDifference, $onePartialIntersection); + } + + public function dataProviderForTwoPartialIntersectionIsDifferenceOfUnionAndSymmetricDifference(): array + { + return [ + [ + new Set(), + new Set(), + ], + [ + new Set(), + new Set(), + new Set(), + ], + [ + new Set([1]), + new Set(), + ], + [ + new Set(), + new Set([1]), + ], + [ + new Set([1]), + new Set([1]), + ], + [ + new Set(), + new Set([1, 2, 3]), + ], + [ + new Set([1, 2, 3]), + new Set(), + ], + [ + new Set([1, 2, 3, 4, 5]), + new Set([2, 3, 4, 5, 6]), + ], + [ + new Set([1, 2, 3, 4, 5]), + new Set([6, 7, 8, 9, 10]), + ], + [ + new Set([2, 3, 4, 5, 6]), + new Set([3, 4, 5, 6, 7]), + ], + [ + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + ], + ]; + } + + /** + * N-partial intersection is equivalent to the common (complete) intersection of these sets. + * + * @test + * @dataProvider dataProviderForNPartialIntersectionIsCompleteIntersection + * @param Set $s + * @param Set ...$others + * @return void + */ + public function testNPartialIntersectionIsCompleteIntersection(Set $s, Set ...$others) + { + // Given + $n = count($others) + 1; + $onePartialIntersection = $s->intersectPartial($n, ...$others); + + // When + $union = $s->intersect(...$others); + + // Then + $this->assertEquals($union, $onePartialIntersection); + } + + public function dataProviderForNPartialIntersectionIsCompleteIntersection(): array + { + return [ + [ + new Set(), + new Set(), + ], + [ + new Set(), + new Set(), + new Set(), + ], + [ + new Set([1]), + new Set(), + ], + [ + new Set(), + new Set([1]), + ], + [ + new Set([1]), + new Set([1]), + ], + [ + new Set([1, 2, 3, 4, 5]), + new Set([2, 3, 4, 5, 6]), + ], + [ + new Set([1, 2, 3, 4, 5]), + new Set([6, 7, 8, 9, 10]), + ], + [ + new Set([1, 2, 3, 4, 5]), + new Set([2, 3, 4, 5, 6]), + new Set([3, 4, 5, 6, 7]), + ], + [ + new Set([1, 2, 3, 4, 5]), + new Set([6, 7, 8, 9, 10]), + new Set([11, 12, 13, 14, 15]), + ], + [ + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + ], + [ + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + new Set(), + ], + [ + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + new Set([3, 4, 5, 6, 7, 8]), + ], + ]; + } + + /** + * For any M > N M-partial intersection always equals to the empty set. + * + * @test + * @dataProvider dataProviderForMPartialIntersectionIsEmptySetWhenMMoreThanN + * @param int $m + * @param Set $s + * @param Set ...$others + * @return void + */ + public function testMPartialIntersectionIsEmptySetWhenMMoreThanN(int $m, Set $s, Set ...$others) + { + // Given + $emptySet = new Set(); + $n = count($others) + 1; + + // When + $this->assertTrue($m > $n); + $partialIntersection = $s->intersectPartial($m, ...$others); + + // Then + $this->assertEquals($emptySet, $partialIntersection); + } + + public function dataProviderForMPartialIntersectionIsEmptySetWhenMMoreThanN(): array + { + return [ + [ + 3, + new Set(), + new Set(), + ], + [ + 4, + new Set(), + new Set(), + ], + [ + 4, + new Set(), + new Set(), + new Set(), + ], + [ + 5, + new Set(), + new Set(), + new Set(), + ], + [ + 3, + new Set([1]), + new Set(), + ], + [ + 4, + new Set([1]), + new Set(), + ], + [ + 3, + new Set(), + new Set([1]), + ], + [ + 4, + new Set(), + new Set([1]), + ], + [ + 3, + new Set([1]), + new Set([1]), + ], + [ + 4, + new Set([1]), + new Set([1]), + ], + [ + 3, + new Set([1, 2, 3, 4, 5]), + new Set([2, 3, 4, 5, 6]), + ], + [ + 4, + new Set([1, 2, 3, 4, 5]), + new Set([2, 3, 4, 5, 6]), + ], + [ + 3, + new Set([1, 2, 3, 4, 5]), + new Set([6, 7, 8, 9, 10]), + ], + [ + 4, + new Set([1, 2, 3, 4, 5]), + new Set([6, 7, 8, 9, 10]), + ], + [ + 4, + new Set([1, 2, 3, 4, 5]), + new Set([2, 3, 4, 5, 6]), + new Set([3, 4, 5, 6, 7]), + ], + [ + 5, + new Set([1, 2, 3, 4, 5]), + new Set([2, 3, 4, 5, 6]), + new Set([3, 4, 5, 6, 7]), + ], + [ + 4, + new Set([1, 2, 3, 4, 5]), + new Set([6, 7, 8, 9, 10]), + new Set([11, 12, 13, 14, 15]), + ], + [ + 5, + new Set([1, 2, 3, 4, 5]), + new Set([6, 7, 8, 9, 10]), + new Set([11, 12, 13, 14, 15]), + ], + [ + 4, + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + ], + [ + 5, + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + ], + [ + 5, + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + new Set(), + ], + [ + 6, + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + new Set(), + ], + [ + 5, + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + new Set([3, 4, 5, 6, 7, 8]), + ], + [ + 6, + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + new Set([3, 4, 5, 6, 7, 8]), + ], + [ + 100, + new Set([1, 2, 3]), + new Set([2, 3, 4, 5]), + new Set([3, 4, 5, 6, 7]), + new Set([3, 4, 5, 6, 7, 8]), + ], + ]; + } } diff --git a/tests/SetTheory/SetOperationsTest.php b/tests/SetTheory/SetOperationsTest.php index e18f9fb04..482fb322f 100644 --- a/tests/SetTheory/SetOperationsTest.php +++ b/tests/SetTheory/SetOperationsTest.php @@ -1524,6 +1524,717 @@ public function testIntersectWithObjects2() $this->assertEquals($expected->asArray(), $A∩B->asArray()); } + /** + * @test + * @dataProvider dataProviderForIntersectPartial + */ + public function testIntersectPartial(array $A, array $B, int $m, array $mpA∩B, Set $R) + { + // Given + $setA = new Set($A); + $setB = new Set($B); + $expected = new Set($mpA∩B); + + // When + $intersection = $setA->intersectPartial($m, $setB); + $intersection_array = $intersection->asArray(); + + // Then + $this->assertEquals($R, $intersection); + $this->assertEquals($expected, $intersection); + $this->assertEquals(count($mpA∩B), count($intersection)); + foreach ($mpA∩B as $member) { + $this->assertArrayHasKey("$member", $intersection_array); + } + foreach ($mpA∩B as $_ => $value) { + if ($value instanceof Set) { + $this->assertEquals($value, $intersection_array["$value"]); + } else { + $this->assertContains($value, $intersection_array); + } + } + } + + public function dataProviderForIntersectPartial(): array + { + $setOneTwo = new Set([1, 2]); + + return [ + [ + [], + [], + 1, + [], + new Set(), + ], + [ + [], + [], + 2, + [], + new Set(), + ], + [ + [], + [], + 3, + [], + new Set(), + ], + [ + [1], + [], + 1, + [1], + new Set([1]), + ], + [ + [1], + [], + 2, + [], + new Set(), + ], + [ + [1], + [], + 3, + [], + new Set(), + ], + [ + [], + [1], + 1, + [1], + new Set([1]), + ], + [ + [], + [1], + 2, + [], + new Set(), + ], + [ + [], + [1], + 3, + [], + new Set(), + ], + [ + [1], + [1], + 1, + [1], + new Set([1]), + ], + [ + [1], + [1], + 2, + [1], + new Set([1]), + ], + [ + [1], + [1], + 3, + [], + new Set(), + ], + [ + [1], + [2], + 1, + [1, 2], + new Set([1, 2]), + ], + [ + [1], + [2], + 2, + [], + new Set(), + ], + [ + [1], + [2], + 3, + [], + new Set(), + ], + [ + [2], + [1], + 1, + [1, 2], + new Set([1, 2]), + ], + [ + [2], + [1], + 2, + [], + new Set(), + ], + [ + [2], + [1], + 3, + [], + new Set(), + ], + [ + [2], + [2], + 1, + [2], + new Set([2]), + ], + [ + [2], + [2], + 2, + [2], + new Set([2]), + ], + [ + [2], + [2], + 3, + [], + new Set(), + ], + [ + [1, 2], + [1, 2], + 1, + [1, 2], + new Set([1, 2]), + ], + [ + [1, 2], + [1, 2], + 2, + [1, 2], + new Set([1, 2]), + ], + [ + [1, 2], + [1, 2], + 3, + [], + new Set(), + ], + [ + [1, 2], + [2, 1], + 1, + [1, 2], + new Set([1, 2]), + ], + [ + [1, 2], + [2, 1], + 2, + [1, 2], + new Set([1, 2]), + ], + [ + [1, 2], + [2, 1], + 3, + [], + new Set(), + ], + [ + [1, 2, 3, 'a', 'b'], + [1, 'a', 'k'], + 1, + [1, 2, 3, 'a', 'b', 'k'], + new Set([1, 2, 3, 'a', 'b', 'k']), + ], + [ + [1, 2, 3, 'a', 'b'], + [1, 'a', 'k'], + 2, + [1, 'a'], + new Set([1, 'a']), + ], + [ + [1, 2, 3, 'a', 'b'], + [1, 'a', 'k'], + 3, + [], + new Set(), + ], + [ + [1, 2, 3, 'a', 'b', new Set([1, 2])], + [1, 'a', 'k'], + 1, + [1, 2, 3, 'a', 'b', 'k', $setOneTwo], + new Set([1, 2, 3, 'a', 'b', 'k', $setOneTwo]), + ], + [ + [1, 2, 3, 'a', 'b', new Set([1, 2])], + [1, 'a', 'k'], + 2, + [1, 'a'], + new Set([1, 'a']), + ], + [ + [1, 2, 3, 'a', 'b', new Set([1, 2])], + [1, 'a', 'k'], + 3, + [], + new Set(), + ], + [ + [1, 2, 3, 'a', 'b'], + [1, 'a', 'k', new Set([1, 2])], + 1, + [1, 2, 3, 'a', 'b', 'k', $setOneTwo], + new Set([1, 2, 3, 'a', 'b', 'k', $setOneTwo]), + ], + [ + [1, 2, 3, 'a', 'b'], + [1, 'a', 'k', new Set([1, 2])], + 2, + [1, 'a'], + new Set([1, 'a']), + ], + [ + [1, 2, 3, 'a', 'b'], + [1, 'a', 'k', new Set([1, 2])], + 3, + [], + new Set(), + ], + [ + [1, 2, 3, 'a', 'b', $setOneTwo], + [1, 'a', 'k', $setOneTwo], + 1, + [1, 2, 3, 'a', 'b', 'k', $setOneTwo], + new Set([1, 2, 3, 'a', 'b', 'k', $setOneTwo]), + ], + [ + [1, 2, 3, 'a', 'b', $setOneTwo], + [1, 'a', 'k', $setOneTwo], + 2, + [1, 'a', $setOneTwo], + new Set([1, 'a', $setOneTwo]), + ], + [ + [1, 2, 3, 'a', 'b', $setOneTwo], + [1, 'a', 'k', $setOneTwo], + 3, + [], + new Set(), + ], + [ + [1, 2, 3, 'a', 'b', new Set()], + [1, 'a', 'k', $setOneTwo], + 1, + [1, 2, 3, 'a', 'b', 'k', new Set(), $setOneTwo], + new Set([1, 2, 3, 'a', 'b', 'k', new Set(), $setOneTwo]), + ], + [ + [1, 2, 3, 'a', 'b', new Set()], + [1, 'a', 'k', $setOneTwo], + 2, + [1, 'a'], + new Set([1, 'a']), + ], + [ + [1, 2, 3, 'a', 'b', new Set()], + [1, 'a', 'k', $setOneTwo], + 3, + [], + new Set(), + ], + [ + [1, 2, 3, 'a', 'b', $setOneTwo], + [1, 'a', 'k', 3.5, -2, '2.4', $setOneTwo], + 1, + [-2, 1, 2, 3, 3.5, '2.4', 'a', 'b', 'k', $setOneTwo], + new Set([-2, 1, 2, 3, 3.5, '2.4', 'a', 'b', 'k', $setOneTwo]), + ], + [ + [1, 2, 3, 'a', 'b', $setOneTwo], + [1, 'a', 'k', -2, '2.4', 3.5, $setOneTwo], + 2, + [1, 'a', $setOneTwo], + new Set([1, 'a', $setOneTwo]), + ], + [ + [1, 2, 3, 'a', 'b', $setOneTwo], + [1, 'a', 'k', -2, '2.4', 3.5, $setOneTwo], + 3, + [], + new Set(), + ], + ]; + } + + /** + * @test + * @dataProvider dataProviderForIntersectPartialMultipleSets + */ + public function testIntersectPartialMultipleSets(array $A, array $B, array $C, int $m, array $mpA∩B∩C, Set $R) + { + // Given + $setA = new Set($A); + $setB = new Set($B); + $setC = new Set($C); + $expected = new Set($mpA∩B∩C); + + // When + $intersection = $setA->intersectPartial($m, $setB, $setC); + $intersection_array = $intersection->asArray(); + + // Then + $this->assertEquals($R, $intersection); + $this->assertEquals($expected, $intersection); + $this->assertEquals(count($mpA∩B∩C), count($intersection)); + foreach ($mpA∩B∩C as $_ => $value) { + if ($value instanceof Set) { + $this->assertEquals($value, $intersection_array["$value"]); + } else { + $this->assertTrue(in_array($value, $intersection_array)); + } + } + } + + public function dataProviderForIntersectPartialMultipleSets(): array + { + $setOneTwo = new Set([1, 2]); + + $gen = static function (array $data) { + return static function () use ($data) { + foreach ($data as $datum) { + yield $datum; + } + }; + }; + $res = static function () { + return fopen('php://input', 'r'); + }; + $clos = static function () { + return static function () { + return 0; + }; + }; + + return [ + [ + [1, 2, 3, 4], + [2, 3, 4, 5], + [3, 4, 5, 6], + 1, + [1, 2, 3, 4, 5, 6], + new Set([1, 2, 3, 4, 5, 6]), + ], + [ + [1, 2, 3, 4], + [2, 3, 4, 5], + [3, 4, 5, 6], + 2, + [2, 3, 4, 5], + new Set([2, 3, 4, 5]), + ], + [ + [1, 2, 3, 4], + [2, 3, 4, 5], + [3, 4, 5, 6], + 3, + [3, 4], + new Set([3, 4]), + ], + [ + [1, 2, 3, 4], + [2, 3, 4, 5], + [3, 4, 5, 6], + 4, + [], + new Set(), + ], + [ + [1, 2, 3, 4, $setOneTwo], + [2, 3, 4, 5, $setOneTwo], + [3, 4, 5, 6, $setOneTwo], + 1, + [1, 2, 3, 4, 5, 6, $setOneTwo], + new Set([1, 2, 3, 4, 5, 6, $setOneTwo]), + ], + [ + [1, 2, 3, 4, $setOneTwo], + [2, 3, 4, 5, $setOneTwo], + [3, 4, 5, 6, $setOneTwo], + 2, + [2, 3, 4, 5, $setOneTwo], + new Set([2, 3, 4, 5, $setOneTwo]), + ], + [ + [1, 2, 3, 4, $setOneTwo], + [2, 3, 4, 5, $setOneTwo], + [3, 4, 5, 6, $setOneTwo], + 3, + [3, 4, $setOneTwo], + new Set([3, 4, $setOneTwo]), + ], + [ + [1, 2, 3, 4, $setOneTwo], + [2, 3, 4, 5, $setOneTwo], + [3, 4, 5, 6, $setOneTwo], + 4, + [], + new Set(), + ], + [ + ['c++', 'java', 'c#', 'go', 'haskell'], + ['php', 'python', 'javascript', 'perl'], + ['c++', 'java', 'c#', 'go', 'php'], + 1, + ['c++', 'java', 'c#', 'go', 'haskell', 'php', 'python', 'javascript', 'perl'], + new Set(['c++', 'java', 'c#', 'go', 'haskell', 'php', 'python', 'javascript', 'perl']), + ], + [ + ['c++', 'java', 'c#', 'go', 'haskell'], + ['php', 'python', 'javascript', 'perl'], + ['c++', 'java', 'c#', 'go', 'php'], + 2, + ['c++', 'java', 'c#', 'go', 'php'], + new Set(['c++', 'java', 'c#', 'go', 'php']), + ], + [ + ['c++', 'java', 'c#', 'go', 'haskell'], + ['php', 'python', 'javascript', 'perl'], + ['c++', 'java', 'c#', 'go', 'php'], + 3, + [], + new Set(), + ], + [ + ['c++', 'java', 'c#', 'go', 'haskell'], + ['php', 'python', 'javascript', 'perl'], + ['c++', 'java', 'c#', 'go', 'php'], + 4, + [], + new Set(), + ], + [ + [1, 2, 3, 4, 5], + [1, 2, '3', '4'], + [4, 5, 6, 7, '8', '9'], + 1, + [1, 2, 3, 4, 5, 6, 7, '8', '9'], + new Set([1, 2, 3, 4, 5, 6, 7, '8', '9']), + ], + [ + [1, 2, 3, 4, 5], + [1, 2, '3', '4'], + [4, 5, 6, 7, '8', '9'], + 2, + [1, 2, 3, 4, 5], + new Set([1, 2, 3, 4, 5]), + ], + [ + [1, 2, 3, 4, 5], + [1, 2, '3', '4'], + [4, 5, 6, 7, '8', '9'], + 3, + [4], + new Set([4]), + ], + [ + [1, 2, 3, 4, 5], + [1, 2, '3', '4'], + [4, 5, 6, 7, '8', '9'], + 4, + [], + new Set(), + ], + [ + [[1], [2], [3], [4], [5]], + [[2], [3], [4], [5], [6]], + [[3], [4], [5], [6], [7]], + 1, + [[1], [2], [3], [4], [5], [6], [7]], + new Set([[1], [2], [3], [4], [5], [6], [7]]), + ], + [ + [[1], [2], [3], [4], [5]], + [[2], [3], [4], [5], [6]], + [[3], [4], [5], [6], [7]], + 2, + [[2], [3], [4], [5], [6]], + new Set([[2], [3], [4], [5], [6]]), + ], + [ + [[1], [2], [3], [4], [5]], + [[2], [3], [4], [5], [6]], + [[3], [4], [5], [6], [7]], + 3, + [[3], [4], [5]], + new Set([[3], [4], [5]]), + ], + [ + [[1], [2], [3], [4], [5]], + [[2], [3], [4], [5], [6]], + [[3], [4], [5], [6], [7]], + 4, + [], + new Set(), + ], + [ + [$o1 = (object)[1], $o2 = (object)[2], $o3 = (object)[3], $o4 = (object)[4], $o5 = (object)[5]], + [$o2, $o3, $o4, $o5, $o6 = (object)[6]], + [$o3, $o4, $o5, $o6, $o7 = (object)[7]], + 1, + [$o1, $o2, $o3, $o4, $o5, $o6, $o7], + new Set([$o1, $o2, $o3, $o4, $o5, $o6, $o7]), + ], + [ + [(object)[1], $o2 = (object)[2], $o3 = (object)[3], $o4 = (object)[4], $o5 = (object)[5]], + [$o2, $o3, $o4, $o5, $o6 = (object)[6]], + [$o3, $o4, $o5, $o6, (object)[7]], + 2, + [$o2, $o3, $o4, $o5, $o6], + new Set([$o2, $o3, $o4, $o5, $o6]), + ], + [ + [(object)[1], $o2 = (object)[2], $o3 = (object)[3], $o4 = (object)[4], $o5 = (object)[5]], + [$o2, $o3, $o4, $o5, $o6 = (object)[6]], + [$o3, $o4, $o5, $o6, (object)[7]], + 3, + [$o3, $o4, $o5], + new Set([$o3, $o4, $o5]), + ], + [ + [(object)[1], $o2 = (object)[2], $o3 = (object)[3], $o4 = (object)[4], $o5 = (object)[5]], + [$o2, $o3, $o4, $o5, $o6 = (object)[6]], + [$o3, $o4, $o5, $o6, (object)[7]], + 4, + [], + new Set(), + ], + [ + [$o1 = $gen([]), $o2 = $gen([]), $o3 = $gen([]), $o4 = $gen([]), $o5 = $gen([])], + [$o2, $o3, $o4, $o5, $o6 = $gen([])], + [$o3, $o4, $o5, $o6, $o7 = $gen([])], + 1, + [$o1, $o2, $o3, $o4, $o5, $o6, $o7], + new Set([$o1, $o2, $o3, $o4, $o5, $o6, $o7]), + ], + [ + [$gen([]), $o2 = $gen([]), $o3 = $gen([]), $o4 = $gen([]), $o5 = $gen([])], + [$o2, $o3, $o4, $o5, $o6 = $gen([])], + [$o3, $o4, $o5, $o6, $gen([])], + 2, + [$o2, $o3, $o4, $o5, $o6], + new Set([$o2, $o3, $o4, $o5, $o6]), + ], + [ + [$gen([]), $o2 = $gen([]), $o3 = $gen([]), $o4 = $gen([]), $o5 = $gen([])], + [$o2, $o3, $o4, $o5, $o6 = $gen([])], + [$o3, $o4, $o5, $o6, $gen([])], + 3, + [$o3, $o4, $o5], + new Set([$o3, $o4, $o5]), + ], + [ + [$gen([]), $o2 = $gen([]), $o3 = $gen([]), $o4 = $gen([]), $o5 = $gen([])], + [$o2, $o3, $o4, $o5, $o6 = $gen([])], + [$o3, $o4, $o5, $o6, $gen([])], + 4, + [], + new Set(), + ], + [ + [$o1 = $res(), $o2 = $res(), $o3 = $res(), $o4 = $res(), $o5 = $res()], + [$o2, $o3, $o4, $o5, $o6 = $res()], + [$o3, $o4, $o5, $o6, $o7 = $res()], + 1, + [$o1, $o2, $o3, $o4, $o5, $o6, $o7], + new Set([$o1, $o2, $o3, $o4, $o5, $o6, $o7]), + ], + [ + [$res(), $o2 = $res(), $o3 = $res(), $o4 = $res(), $o5 = $res()], + [$o2, $o3, $o4, $o5, $o6 = $res()], + [$o3, $o4, $o5, $o6, $res()], + 2, + [$o2, $o3, $o4, $o5, $o6], + new Set([$o2, $o3, $o4, $o5, $o6]), + ], + [ + [$res(), $o2 = $res(), $o3 = $res(), $o4 = $res(), $o5 = $res()], + [$o2, $o3, $o4, $o5, $o6 = $res()], + [$o3, $o4, $o5, $o6, $res()], + 3, + [$o3, $o4, $o5], + new Set([$o3, $o4, $o5]), + ], + [ + [$res(), $o2 = $res(), $o3 = $res(), $o4 = $res(), $o5 = $res()], + [$o2, $o3, $o4, $o5, $o6 = $res()], + [$o3, $o4, $o5, $o6, $res()], + 4, + [], + new Set(), + ], + [ + [$o1 = $clos(), $o2 = $clos(), $o3 = $clos(), $o4 = $clos(), $o5 = $clos()], + [$o2, $o3, $o4, $o5, $o6 = $clos()], + [$o3, $o4, $o5, $o6, $o7 = $clos()], + 1, + [$o1, $o2, $o3, $o4, $o5, $o6, $o7], + new Set([$o1, $o2, $o3, $o4, $o5, $o6, $o7]), + ], + [ + [$clos(), $o2 = $clos(), $o3 = $clos(), $o4 = $clos(), $o5 = $clos()], + [$o2, $o3, $o4, $o5, $o6 = $clos()], + [$o3, $o4, $o5, $o6, $clos()], + 2, + [$o2, $o3, $o4, $o5, $o6], + new Set([$o2, $o3, $o4, $o5, $o6]), + ], + [ + [$clos(), $o2 = $clos(), $o3 = $clos(), $o4 = $clos(), $o5 = $clos()], + [$o2, $o3, $o4, $o5, $o6 = $clos()], + [$o3, $o4, $o5, $o6, $clos()], + 3, + [$o3, $o4, $o5], + new Set([$o3, $o4, $o5]), + ], + [ + [$clos(), $o2 = $clos(), $o3 = $clos(), $o4 = $clos(), $o5 = $clos()], + [$o2, $o3, $o4, $o5, $o6 = $clos()], + [$o3, $o4, $o5, $o6, $clos()], + 4, + [], + new Set(), + ], + [ + [null, 1, 2, 3], + [1, 2, 3, 4, 5], + [2, 3, 4], + 2, + [1, 2, 3, 4], + new Set([1, 2, 3, 4]), + ], + [ + [null, 1, 2, 3], + [null, 1, 2, 3, 4, 5], + [2, 3, 4], + 2, + [null, 1, 2, 3, 4], + new Set([null, 1, 2, 3, 4]), + ], + ]; + } + /** * @test * @dataProvider dataProviderForDifference