Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set::intersectPartial() method added to the SetTheory namespace #464

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
61 changes: 61 additions & 0 deletions src/SetTheory/Set.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -374,6 +377,7 @@ public function isProperSuperset(Set $B): bool
* SET OPERATIONS ON OTHER SETS
* - Union
* - Intersection
* - Partial intersection
* - Difference
* - Symmetric difference
**************************************************************************/
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion src/Util/Iter.php
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
95 changes: 95 additions & 0 deletions src/Util/JustifyMultipleIterator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

namespace MathPHP\Util;

/**
* @implements \Iterator<array<mixed>>
*
* 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<mixed>>
*/
protected $iterators = [];
/**
* @var int
*/
protected $index = 0;

/**
* @param iterable<mixed> ...$iterables
*/
public function __construct(iterable ...$iterables)
{
foreach ($iterables as $iterable) {
$this->iterators[] = Iter::makeIterator($iterable);
}
}

/**
* {@inheritDoc}
*
* @return array<mixed>
*/
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;
}
}
29 changes: 29 additions & 0 deletions src/Util/NoValueMonad.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace MathPHP\Util;

/**
* Based on IterTools PHP's IteratorFactory.
* @see https://github.com/markrogoyski/itertools-php
* @see https://github.com/markrogoyski/itertools-php/blob/main/src/Util/NoValueMonad.php
*/
class NoValueMonad
{
/**
* @var self|null
*/
private static $instance = null;

public static function getInstance(): self
{
if (self::$instance === null) {
self::$instance = new self();
}

return self::$instance;
}

private function __construct()
{
}
}
Loading