Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
100.00% |
1 / 1 |
|
100.00% |
7 / 7 |
CRAP | |
100.00% |
30 / 30 |
CartesianProduct | |
100.00% |
1 / 1 |
|
100.00% |
7 / 7 |
18 | |
100.00% |
30 / 30 |
__construct | |
100.00% |
1 / 1 |
3 | |
100.00% |
6 / 6 |
|||
addSet | |
100.00% |
1 / 1 |
4 | |
100.00% |
7 / 7 |
|||
current | |
100.00% |
1 / 1 |
2 | |
100.00% |
4 / 4 |
|||
key | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
next | |
100.00% |
1 / 1 |
4 | |
100.00% |
7 / 7 |
|||
rewind | |
100.00% |
1 / 1 |
2 | |
100.00% |
4 / 4 |
|||
valid | |
100.00% |
1 / 1 |
2 | |
100.00% |
1 / 1 |
<?php declare(strict_types = 1); | |
/** | |
* @package: pvc | |
* @author: Doug Wilbourne (dougwilbourne@gmail.com) | |
* @version: 1.0 | |
*/ | |
namespace pvc\array_utils\CartesianProduct; | |
use ArrayIterator; | |
use Iterator; | |
/** | |
* This is a modification of code found more or less here: | |
* https://github.com/hoaproject/Math/blob/master/Source/Combinatorics/Combination/CartesianProduct.php | |
* | |
* It was published with a BSD license and that license is included below. | |
* | |
*/ | |
/** | |
* Hoa | |
* | |
* | |
* @license | |
* | |
* New BSD License | |
* | |
* Copyright © 2007-2018, Hoa community. All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions are met: | |
* * Redistributions of source code must retain the above copyright | |
* notice, this list of conditions and the following disclaimer. | |
* * Redistributions in binary form must reproduce the above copyright | |
* notice, this list of conditions and the following disclaimer in the | |
* documentation and/or other materials provided with the distribution. | |
* * Neither the name of the Hoa nor the names of its contributors may be | |
* used to endorse or promote products derived from this software without | |
* specific prior written permission. | |
* | |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE | |
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
* POSSIBILITY OF SUCH DAMAGE. | |
*/ | |
/** | |
* Cartesian product creates a cross product, or Cartesian product, of elements from different sets. | |
* | |
* It is an iterator that produces tuples that represent all possible combinations of "one from set a, | |
* one from set b...." etc. | |
* | |
* | |
* Class CartesianProduct | |
* | |
*/ | |
class CartesianProduct implements Iterator | |
{ | |
/** | |
* | |
* array of iterators used to create the cartesian product. It is set at construction time and | |
* cannot be modified after that, because doing in the middle of iterating would produce inconsistent element | |
* dimensionality between the last set produced prior to the addition / subtraction and the next tuple after. | |
* | |
* @var Iterator[] | |
*/ | |
protected array $arrayOfIterators = []; | |
/** | |
* In order to conform to the Iterator interface, key has to be an integer. But you can think of each | |
* incremented key as mapping to an array which has the same length as the arrayOfIterators, and each | |
* element of the array would be an integer that corresponds to the current position in the corresponding iterator. | |
* | |
* @var int | |
*/ | |
protected int $key = 0; | |
/** | |
* reference to the last iterator in the arrayOfIterators, which is used to determine whether the internal | |
* pointers are valid | |
* | |
* @var Iterator | |
*/ | |
protected Iterator $lastIterator; | |
/** | |
* The constructor creates an array of iterators, which gives us access to the current position in each array. | |
* | |
* CartesianProduct constructor. | |
* | |
* @param array $iterators | |
* @throws CartesianProductException | |
*/ | |
public function __construct(array $iterators) | |
{ | |
if (count($iterators) == 0) { | |
throw new CartesianProductException(); | |
} | |
for ($i = 0; $i < count($iterators); $i++) { | |
$this->addSet($iterators[$i]); | |
} | |
$this->lastIterator = $this->arrayOfIterators[$i - 1]; | |
} | |
/** | |
* @function addSet | |
* @param array|Iterator $set | |
* @throws CartesianProductException | |
*/ | |
protected function addSet($set) : void | |
{ | |
if (is_array($set)) { | |
$set = new ArrayIterator($set); | |
} | |
if (!$set instanceof Iterator || 0 == iterator_count($set)) { | |
throw new CartesianProductException(); | |
} | |
$set->rewind(); | |
$this->arrayOfIterators[] = $set; | |
} | |
/** | |
* @function current | |
* | |
* Get the current tuple. | |
* | |
* @return array | |
*/ | |
public function current(): array | |
{ | |
$currentTuple = []; | |
foreach ($this->arrayOfIterators as $iterator) { | |
$currentTuple[] = $iterator->current(); | |
} | |
return $currentTuple; | |
} | |
/** | |
* @function key | |
* | |
* Get the current key. | |
* | |
* @return int | |
*/ | |
public function key(): int | |
{ | |
return $this->key; | |
} | |
/** | |
* @function next | |
* | |
* Advance the internal pointers. | |
* | |
*/ | |
public function next(): void | |
{ | |
foreach ($this->arrayOfIterators as $iterator) { | |
$iterator->next(); | |
if (!$iterator->valid() && ($iterator != $this->lastIterator)) { | |
$iterator->rewind(); | |
} else { | |
// break the foreach loop - $iterator is valid or it is the last iterator | |
// and the last iterator is invalid. | |
break; | |
} | |
} | |
$this->key++; | |
} | |
/** | |
* @function rewind | |
* | |
* Rewind the internal pointers. | |
*/ | |
public function rewind() : void | |
{ | |
$this->key = 0; | |
foreach ($this->arrayOfIterators as $iterator) { | |
$iterator->rewind(); | |
} | |
} | |
/** | |
* @function valid | |
* | |
* The iterator is in a valid state if the lastIterator exists (meaning the arrayOfIterators is not empty) | |
* and the last iterator is itself valid. | |
* | |
* @return bool | |
*/ | |
public function valid(): bool | |
{ | |
return (isset($this->lastIterator) && $this->lastIterator->valid()); | |
} | |
} |