Completed
Push — master ( a4ec79...a5f70a )
by Pol
03:15
created

Permutations::getElements()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
namespace drupol\phpermutations;
4
5
/**
6
 * Class Permutations.
7
 *
8
 * @package drupol\phpermutations
9
 */
10
class Permutations {
11
12
  /**
13
   * @var mixed
14
   */
15
  protected $elements;
16
17
  /**
18
   * @var int
19
   */
20
  protected $size;
21
22
  /**
23
   * Permutations constructor.
24
   *
25
   * @param array $elements
26
   * @param int $size
0 ignored issues
show
Documentation introduced by
Should the type for parameter $size not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
27
   */
28 2
  public function __construct(array $elements, $size = NULL) {
1 ignored issue
show
Coding Style introduced by
TRUE, FALSE and NULL must be lowercase; expected null, but found NULL.
Loading history...
29 2
    $this->setElements($elements);
30 2
    $size = $size ? $size : count($elements);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $size. This often makes code more readable.
Loading history...
31 2
    $this->setSize($size);
32
33 2
    return $this;
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
34
  }
35
36
  /**
37
   * @param int $size
38
   */
39 2
  public function setSize($size) {
40 2
    $this->size = $size;
41 2
  }
42
43
  /**
44
   * @return int
45
   */
46 2
  public function getSize() {
47 2
    return (int) $this->size;
48
  }
49
50
  /**
51
   * @param array $elements
52
   *
53
   * @return $this
54
   */
55 2
  public function setElements(array $elements) {
56 2
    $this->elements = $elements;
57
58 2
    return $this;
59
  }
60
61
  /**
62
   * @return mixed
63
   */
64 2
  public function getElements() {
65 2
    return $this->elements;
66
  }
67
68
  /**
69
   * @return \Generator
70
   */
71 2
  public function generator() {
72 2
    return $this->generate($this->getElements(), $this->getSize());
73
  }
74
75
  /**
76
   * @param array $elements
77
   * @param $subset_length
78
   *
79
   * @return \Generator
80
   */
81 2
  protected function generate(array $elements, $subset_length) {
0 ignored issues
show
Coding Style Naming introduced by
The parameter $subset_length is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style Naming introduced by
The variable $subset_length is not named in camelCase.

This check marks variable names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
82 2
    $length = \count($elements);
83 2
    if($subset_length === null){
0 ignored issues
show
Coding Style introduced by
Expected 1 space after IF keyword; 0 found
Loading history...
84
      $subset_length = $length;
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $subset_length. This often makes code more readable.
Loading history...
85
    }
86 2
    if($subset_length > $length){
0 ignored issues
show
Coding Style introduced by
Expected 1 space after IF keyword; 0 found
Loading history...
87
      $subset_length = $length;
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $subset_length. This often makes code more readable.
Loading history...
88
    }
89 2
    $pools = [];
90 2
    for ($i = 0; $i < $subset_length; $i++) {
91 2
      $pools[] = $elements;
92 2
    }
93 2
    $productGenerator = $this->product($pools);
94 2
    foreach($productGenerator as $key => $indices){
0 ignored issues
show
Coding Style introduced by
Expected 1 space after FOREACH keyword; 0 found
Loading history...
95 2
      $flipIndices = array_keys(array_flip($indices));
96 2
      if(\count($flipIndices) == $subset_length){
0 ignored issues
show
Coding Style introduced by
Expected 1 space after IF keyword; 0 found
Loading history...
97 2
        $permute = [];
98 2
        foreach($indices as $k => $i){
0 ignored issues
show
Coding Style introduced by
Expected 1 space after FOREACH keyword; 0 found
Loading history...
99 2
          $permute[] = $elements[$key[$k]];
100 2
        }
101 2
        yield $key => $permute;
102 2
      }
103 2
    }
104 2
  }
105
106
  /**
107
   * Returns the cartesian product of iterables that were passed as arguments.
108
   *
109
   * The resulting iterator will contain all the possible tuples of keys and
110
   * values.
111
   *
112
   * @param array|Traversable $iterables
113
   *   Iterables to combine
114
   *
115
   * @return \Iterator
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use \Generator.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
116
   */
117 2
  function product($iterables) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Comprehensibility Best Practice introduced by
It is recommend to declare an explicit visibility for product.

Generally, we recommend to declare visibility for all methods in your source code. This has the advantage of clearly communication to other developers, and also yourself, how this method should be consumed.

If you are not sure which visibility to choose, it is a good idea to start with the most restrictive visibility, and then raise visibility as needed, i.e. start with private, and only raise it to protected if a sub-class needs to have access, or public if an external class needs access.

Loading history...
118
    /** @var \Iterator[] $iterators */
119 2
    $iterators = array_map(array($this, 'toIter'), $iterables);
120 2
    $numIterators = count($iterators);
121 2
    if (!$numIterators) {
122
      yield [] => [];
123
      return;
124
    }
125 2
    $keyTuple = $valueTuple = array_fill(0, $numIterators, null);
126 2
    $i = -1;
127 2
    while (true) {
128 2
      while (++$i < $numIterators - 1) {
129 2
        $iterators[$i]->rewind();
130 2
        if (!$iterators[$i]->valid()) {
131
          return;
132
        }
133 2
        $keyTuple[$i] = $iterators[$i]->key();
134 2
        $valueTuple[$i] = $iterators[$i]->current();
135 2
      }
136 2
      foreach ($iterators[$i] as $keyTuple[$i] => $valueTuple[$i]) {
137 2
        yield $keyTuple => $valueTuple;
138 2
      }
139 2
      while (--$i >= 0) {
140 2
        $iterators[$i]->next();
141 2
        if ($iterators[$i]->valid()) {
142 2
          $keyTuple[$i] = $iterators[$i]->key();
143 2
          $valueTuple[$i] = $iterators[$i]->current();
144 2
          continue 2;
145
        }
146 2
      }
147 2
      return;
148
    }
149
  }
150
151
  /**
152
   * Converts any iterable into an Iterator.
153
   *
154
   * @param array|Traversable $iterable
155
   *   The iterable to turn into an iterator.
156
   *
157
   * @return \Iterator
158
   */
159 2
  function toIter($iterable) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Comprehensibility Best Practice introduced by
It is recommend to declare an explicit visibility for toIter.

Generally, we recommend to declare visibility for all methods in your source code. This has the advantage of clearly communication to other developers, and also yourself, how this method should be consumed.

If you are not sure which visibility to choose, it is a good idea to start with the most restrictive visibility, and then raise visibility as needed, i.e. start with private, and only raise it to protected if a sub-class needs to have access, or public if an external class needs access.

Loading history...
160 2
    if ($iterable instanceof \Iterator) {
161
      return $iterable;
162
    }
163 2
    if ($iterable instanceof \IteratorAggregate) {
164
      return $iterable->getIterator();
165
    }
166 2
    if (is_array($iterable)) {
167 2
      return new \ArrayIterator($iterable);
168
    }
169
    throw new \InvalidArgumentException('Argument must be iterable');
170
  }
171
172
  /**
173
   * @return array
174
   */
175 2
  public function toArray() {
176 2
    $results = array();
177
178 2
    foreach($this->generator() as $value) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after FOREACH keyword; 0 found
Loading history...
179 2
      $results[] = $value;
180 2
    }
181
182 2
    return $results;
183
  }
184
185
}
186