Page Menu
Home
DevCentral
Search
Configure Global Search
Log In
Files
F24682867
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
28 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/Collections/BaseVector.php b/src/Collections/BaseVector.php
index 8c459e6..cc4b9a6 100644
--- a/src/Collections/BaseVector.php
+++ b/src/Collections/BaseVector.php
@@ -1,253 +1,283 @@
<?php
declare(strict_types=1);
namespace Keruald\OmniTools\Collections;
use ArrayAccess;
use ArrayIterator;
use InvalidArgumentException;
use IteratorAggregate;
use Traversable;
use Keruald\OmniTools\Reflection\CallableElement;
use Keruald\OmniTools\Strings\Multibyte\OmniString;
abstract class BaseVector extends BaseCollection implements ArrayAccess, IteratorAggregate {
///
/// Properties
///
protected array $items;
///
/// Constructors
///
public function __construct (iterable $items = []) {
if (is_array($items)) {
$this->items = $items;
return;
}
foreach ($items as $item) {
$this->items[] = $item;
}
}
public static function from (iterable $items) : static {
return new static($items);
}
///
/// Interact with collection content at key level
///
public function get (int $key) : mixed {
if (!array_key_exists($key, $this->items)) {
throw new InvalidArgumentException("Key not found.");
}
return $this->items[$key];
}
public function getOr (int $key, mixed $defaultValue) : mixed {
return $this->items[$key] ?? $defaultValue;
}
public function set (int $key, mixed $value) : static {
$this->items[$key] = $value;
return $this;
}
public function unset (int $key) : static {
unset($this->items[$key]);
return $this;
}
public function contains (mixed $value) : bool {
return in_array($value, $this->items);
}
///
/// Interact with collection content at collection level
///
public function count () : int {
return count($this->items);
}
public function isEmpty () : bool {
return $this->count() === 0;
}
public function clear () : self {
$this->items = [];
return $this;
}
public function push (mixed $item) : self {
$this->items[] = $item;
return $this;
}
/**
* Append all elements of the specified iterable
* to the current vector.
*
* If a value already exists, the value is still added
* as a duplicate.
*
* @see update() when you need to only add unique values.
*/
public function append (iterable $iterable) : self {
foreach ($iterable as $value) {
$this->items[] = $value;
}
return $this;
}
/**
* Append all elements of the specified iterable
* to the current vector.
*
* If a value already exists, it is skipped.
*
* @see append() when you need to always add everything.
*/
public function update (iterable $iterable) : self {
foreach ($iterable as $value) {
if (!$this->contains($value)) {
$this->items[] = $value;
}
}
return $this;
}
+ /**
+ * Replaces a part of the vector by the specified iterable.
+ *
+ * @param int $offset Allow to replace a part inside the vector by an iterable with keys starting at 0, by adding the specified offset.
+ * @param int $len The maximum amount of elements to read. If 0, the read isn't bounded.
+ */
+ public function replace(iterable $iterable, int $offset = 0, int $len = 0) : self {
+ $itemsCount = 0;
+
+ foreach ($iterable as $key => $value) {
+ $this->items[$key + $offset] = $value;
+
+ $itemsCount++;
+ if ($len > 0 && $itemsCount >= $len) {
+ break;
+ }
+ }
+
+ return $this;
+ }
+
/**
* Gets a copy of the internal vector.
*
* Scalar values (int, strings) are cloned.
* Objects are references to a specific objet, not a clone.
*
* @return array
*/
public function toArray () : array {
return $this->items;
}
///
/// HOF :: generic
///
public function map (callable $callable) : self {
return new static(array_map($callable, $this->items));
}
public function filter (callable $callable) : self {
$argc = (new CallableElement($callable))->countArguments();
if ($argc === 0) {
throw new InvalidArgumentException(
"Callback should have at least one argument"
);
}
$mode = (int)($argc > 1);
return new static(array_filter($this->items, $callable, $mode));
}
public function mapKeys (callable $callable) : self {
$mappedVector = [];
foreach ($this->items as $key => $value) {
$mappedVector[$callable($key)] = $value;
}
return new static($mappedVector);
}
public function flatMap (callable $callable) : self {
$argc = (new CallableElement($callable))->countArguments();
$newMap = new static;
foreach ($this->items as $key => $value) {
$toAdd = match($argc) {
0 => throw new InvalidArgumentException(self::CB_ZERO_ARG),
1 => $callable($value),
default => $callable($key, $value),
};
$newMap->append($toAdd);
}
return $newMap;
}
public function filterKeys (callable $callable) : self {
return new static(
array_filter($this->items, $callable, ARRAY_FILTER_USE_KEY)
);
}
+ public function chunk (int $length): Vector {
+ return new Vector(array_chunk($this->items, $length));
+ }
+
+ public function slice (int $offset, int $length) : self {
+ $slice = array_slice($this->items, $offset, $length);
+ return new static($slice);
+ }
+
public function implode(string $delimiter) : OmniString {
return new OmniString(implode($delimiter, $this->items));
}
///
/// ArrayAccess
/// Interface to provide accessing objects as arrays.
///
private static function ensureOffsetIsInteger (mixed $offset) {
if (is_int($offset)) {
return;
}
throw new InvalidArgumentException(
"Offset of a vector must be an integer."
);
}
public function offsetExists (mixed $offset) : bool {
self::ensureOffsetIsInteger($offset);
return array_key_exists($offset, $this->items);
}
public function offsetGet (mixed $offset) : mixed {
self::ensureOffsetIsInteger($offset);
return $this->get($offset);
}
public function offsetSet (mixed $offset, mixed $value) : void {
if ($offset === null) {
$this->push($value);
return;
}
self::ensureOffsetIsInteger($offset);
$this->set($offset, $value);
}
public function offsetUnset (mixed $offset) : void {
self::ensureOffsetIsInteger($offset);
$this->unset($offset);
}
///
/// IteratorAggregate
///
public function getIterator () : Traversable {
return new ArrayIterator($this->items);
}
}
diff --git a/src/Collections/BitsVector.php b/src/Collections/BitsVector.php
new file mode 100644
index 0000000..1cc1a21
--- /dev/null
+++ b/src/Collections/BitsVector.php
@@ -0,0 +1,230 @@
+<?php
+declare(strict_types=1);
+
+namespace Keruald\OmniTools\Collections;
+
+use Exception;
+use InvalidArgumentException;
+
+class BitsVector extends BaseVector {
+
+ ///
+ /// Constructors
+ ///
+
+ public static function new (int $capacity) : self {
+ return match (true) {
+ $capacity === 0 => new self([]),
+ $capacity < 0 => throw new InvalidArgumentException("Capacity must be a positive number"),
+ default => new self(array_fill(0, $capacity, 0)),
+ };
+ }
+
+ public static function fromInteger (int $n) : self {
+ return self::fromBinaryString(decbin($n));
+ }
+
+ public static function fromBinaryString (string $bits) : self {
+ $vector = ArrayUtilities::toIntegers(str_split($bits));
+ return new self($vector);
+ }
+
+ public static function fromHexString (string $number) : self {
+ $bits = array_map(
+ fn($n) => str_pad(decbin(sscanf(implode($n), "%x")[0]), 8, "0", STR_PAD_LEFT),
+ array_chunk(str_split($number), 2)
+ );
+
+ return self::fromBinaryString(implode($bits));
+ }
+
+ public static function fromDecoratedHexString (string $expression) : self {
+ $number = preg_replace("/[^a-fA-F0-9]/", "", $expression);
+ return self::fromHexString($number);
+ }
+
+ public static function fromString (string $string) : self {
+ return BitsVector::fromHexString(bin2hex($string));
+ }
+
+ /**
+ * @throws Exception if an appropriate source of randomness cannot be found.
+ */
+ public static function random (int $length) : self {
+ if ($length < 0) {
+ throw new InvalidArgumentException("The length must be a positive value");
+ }
+
+ if ($length === 0) {
+ return new self([]);
+ }
+
+ $randomBytes = random_bytes((int)ceil($length / 8));
+
+ return BitsVector::fromString($randomBytes)->truncate($length);
+ }
+
+ ///
+ /// Specialized methods to work with bits vector
+ ///
+
+ public function toBinaryString () : string {
+ return implode($this->items);
+ }
+
+ public function toInteger () : int {
+ if ($this->count() > 63) {
+ throw new InvalidArgumentException("PHP doesn't allow representing integers greater than 64 bits.");
+ }
+
+ return bindec($this->toBinaryString());
+ }
+
+ private static function bitsToHex (array $bits) : string {
+ $number = implode("", $bits);
+ return base_convert($number, 2, 16);
+ }
+
+ public function toHexString () : string {
+ $expectedLen = (int)ceil($this->count() / 4);
+
+ return $this
+ ->chunk(4)
+ ->map(fn($chunk) => self::bitsToHex($chunk))
+ ->implode("")
+ ->pad($expectedLen, "0", STR_PAD_RIGHT)
+ ;
+ }
+
+ public function toBytesArray () : array {
+ if ($this->count() % 8 !== 0) {
+ throw new InvalidArgumentException("This vector can't be represented in bytes: the bits count is not a multiple of 8.");
+ }
+
+ return $this
+ ->chunk(8)
+ ->map(fn ($chunk) => bindec(implode($chunk)))
+ ->toArray();
+ }
+
+ /**
+ * Pad the vector with 0 at the leftmost position ("zerofill")
+ *
+ * @param int $length The expected length of the vector
+ */
+ public function pad (int $length) : self {
+ $currentCount = $this->count();
+
+ if ($currentCount >= $length) {
+ return $this;
+ }
+
+ $bits = array_fill(0, $length - $currentCount, 0);
+ array_push($bits, ...$this->items);
+
+ $this->items = $bits;
+ return $this;
+ }
+
+ public function truncate (int $length) : self {
+ if ($this->count() <= $length) {
+ return $this;
+ }
+
+ $bits = array_slice($this->items, 0, $length);
+
+ $this->items = $bits;
+ return $this;
+ }
+
+ /**
+ * Truncate or pad the array as needed to ensure a specified length.
+ */
+ public function shapeCapacity (int $length) : self {
+ $currentCount = $this->count();
+
+ if ($currentCount == $length) {
+ return $this;
+ }
+
+ if ($currentCount < $length) {
+ return $this->pad($length);
+ }
+
+ return $this->truncate($length);
+ }
+
+ /**
+ * Copy the bits of the specified integer to the bits vector
+ * at the specified offset.
+ *
+ * @param int $integer The integer to copy bits from
+ * @param int $offset The position in the vector where to start to copy
+ * @param int $length The length the bits of the integer occupy in the vector
+ */
+ public function copyInteger (int $integer, int $offset, int $length) : self {
+ $toInsert = self::fromInteger($integer)
+ ->shapeCapacity($length);
+
+ return $this->replace($toInsert, $offset, $length);
+ }
+
+ ///
+ /// Ensure value is always 0 or 1 bit
+ ///
+
+ private static function assertValueIsBit (mixed $value) : void {
+ if ($value !== 0 && $value !== 1) {
+ throw new InvalidArgumentException("Only 0 and 1 are accepted as bit value.");
+ }
+ }
+
+ private static function assertValueIsBitsIterable (iterable $bits) {
+ foreach ($bits as $bit) {
+ self::assertValueIsBit($bit);
+ }
+ }
+
+ ///
+ /// Override of BaseVector
+ /// Ensure value is always 0 or 1 bit
+ ///
+ /// This section is the cost to pay for not having generics
+ ///
+
+ public function __construct (iterable $bits = []) {
+ self::assertValueIsBitsIterable($bits);
+ parent::__construct($bits);
+ }
+
+ public function set (int $key, mixed $value) : static {
+ self::assertValueIsBit($value);
+ return parent::set($key, $value);
+ }
+
+ public function contains (mixed $value) : bool {
+ self::assertValueIsBit($value);
+ return parent::contains($value);
+ }
+
+ public function push (mixed $item) : self {
+ self::assertValueIsBit($item);
+ return parent::push($item);
+ }
+
+ public function append (iterable $iterable) : self {
+ self::assertValueIsBitsIterable($iterable);
+ return parent::append($iterable);
+ }
+
+ public function update (iterable $iterable) : self {
+ self::assertValueIsBitsIterable($iterable);
+ return parent::update($iterable);
+ }
+
+ public function offsetSet (mixed $offset, mixed $value) : void {
+ self::assertValueIsBit($value);
+ parent::offsetSet($offset, $value);
+ }
+
+}
diff --git a/tests/Collections/BitsVectorTest.php b/tests/Collections/BitsVectorTest.php
new file mode 100644
index 0000000..cc5d1bb
--- /dev/null
+++ b/tests/Collections/BitsVectorTest.php
@@ -0,0 +1,233 @@
+<?php
+
+namespace Keruald\OmniTools\Tests\Collections;
+
+use InvalidArgumentException;
+
+use Keruald\OmniTools\Collections\BitsVector;
+use PHPUnit\Framework\TestCase;
+
+class BitsVectorTest extends TestCase {
+
+ ///
+ /// Constructors
+ ///
+
+ public function testConstructorWithInvalidIterable () : void {
+ $this->expectException(InvalidArgumentException::class);
+ new BitsVector([1, 2, 3, 4]);
+ }
+
+ public function testNew () : void {
+ $bits = BitsVector::new(8);
+ $this->assertEquals(8, $bits->count());
+ }
+
+ public function testNewWhenCapacityIsEmpty () : void {
+ $bits = BitsVector::new(0);
+ $this->assertEquals(0, $bits->count());
+ }
+
+ public function testNewWhenCapacityIsNegative () : void {
+ $this->expectException(InvalidArgumentException::class);
+ BitsVector::new(-8);
+ }
+
+ public function testFromInteger () : void {
+ $bits = BitsVector::fromInteger(4);
+ $this->assertEquals([1, 0, 0], $bits->toArray());
+ }
+
+ public function testFromIntegerWhenValueIsNegative () : void {
+ $bits = BitsVector::fromInteger(-1);
+
+ $expected = array_fill(0, 64, 1);
+ $this->assertEquals($expected, $bits->toArray());
+ }
+
+ public function testFromBinString () : void {
+ $bits = BitsVector::fromBinaryString("1001");
+ $this->assertEquals([1, 0, 0, 1], $bits->toArray());
+ }
+
+ public function testFromHexString () : void {
+ $bits = BitsVector::fromHexString("337362ea");
+ $this->assertEquals("337362ea", $bits->toHexString());
+ }
+
+ public function testFromDecoratedHexString () : void {
+ $bits = BitsVector::fromDecoratedHexString("cc4eca23-b825-11ec-ab20-a81e84f35d9c");
+ $this->assertEquals("cc4eca23b82511ecab20a81e84f35d9c", $bits->toHexString());
+ }
+
+ public function testFromString () : void {
+ // Exemple based on pack() documentation
+ $binaryData = pack("nvc*", 0x1234, 0x5678, 65, 66);
+ $bits = BitsVector::fromString($binaryData);
+
+ $this->assertEquals(
+ [0x12, 0x34, 0x78, 0x56, 0x41, 0x42],
+ $bits->toBytesArray(),
+ );
+ }
+
+ public function testBytesArray () : void {
+ $bits = BitsVector::new(16)
+ ->copyInteger(1, 0, 4)
+ ->copyInteger(2, 4, 4)
+ ->copyInteger(3, 8, 4)
+ ->copyInteger(4, 12, 4);
+
+ $this->assertEquals(
+ [0x12, 0x34],
+ $bits->toBytesArray(),
+ );
+ }
+
+ public function provideLengths () : iterable {
+ yield [1];
+ yield [2];
+ yield [8];
+
+ yield [500];
+ yield [5000];
+ yield [0];
+ }
+
+ /**
+ * @dataProvider provideLengths
+ */
+ public function testRandom($length) : void {
+ $bits = BitsVector::random($length);
+ $this->assertEquals($length, $bits->count());
+ }
+
+ public function testRandomWithNegativeLength() : void {
+ $this->expectException(InvalidArgumentException::class);
+
+ BitsVector::random(-1);
+ }
+
+ public function testBytesArrayWithBadLength () : void {
+ $this->expectException(InvalidArgumentException::class);
+
+ $bits = new BitsVector([1, 1, 1]); // 3 bits isn't a byte
+ $bits->toBytesArray();
+ }
+
+ public function testToBinaryString () : void {
+ $bits = new BitsVector([1, 0, 0, 1]);
+ $this->assertEquals("1001", $bits->toBinaryString());
+ }
+
+ public function testToInteger () : void {
+ $bits = new BitsVector([1, 0, 0, 1]);
+ $this->assertEquals(9, $bits->toInteger());
+ }
+
+ public function testToIntegerWhenThereIsTooMuchBits () : void {
+ $this->expectException(InvalidArgumentException::class);
+
+ BitsVector::new(66)->toInteger();
+ }
+
+ public function testPad () : void {
+ $bits = new BitsVector([1, 0, 0, 1]);
+ $bits->pad(8);
+ $this->assertEquals([0, 0, 0, 0, 1, 0, 0, 1], $bits->toArray());
+ }
+
+ public function testPadWithLargeEnoughCount () : void {
+ $bits = new BitsVector([1, 0, 0, 1]);
+ $bits->pad(4);
+ $this->assertEquals([1, 0, 0, 1], $bits->toArray());
+ }
+
+ public function testTruncate () : void {
+ $bits = new BitsVector([1, 0, 0, 1, 0, 0, 0, 0]);
+ $bits->truncate(4);
+
+ $this->assertEquals([1, 0, 0, 1], $bits->toArray());
+ }
+
+ public function testTruncateWithSmallEnoughCount () : void {
+ $bits = new BitsVector([1, 0, 0, 1]);
+ $bits->truncate(4);
+
+ $this->assertEquals([1, 0, 0, 1], $bits->toArray());
+ }
+
+ public function provideShapeArrays () : iterable {
+ yield [[1, 0, 0, 1, 0, 0, 0, 0], 4, [1, 0, 0, 1]];
+ yield [[1, 0, 0, 1], 4, [1, 0, 0, 1]];
+ yield [[1, 0, 0, 1], 3, [1, 0, 0]];
+
+ yield [[1, 0, 0, 1], 0, []];
+ yield [[], 0, []];
+ yield [[], 4, [0, 0, 0, 0]];
+ }
+
+ /**
+ * @dataProvider provideShapeArrays
+ */
+ public function testShapeCapacity (array $initial, int $length, array $final) : void {
+ $bits = new BitsVector($initial);
+ $bits->shapeCapacity($length);
+
+ $this->assertEquals($final, $bits->toArray());
+ }
+
+ public function testCopyInteger() : void {
+ $bits = BitsVector::new(8);
+ $bits->copyInteger(5, 2, 3);
+
+ $this->assertEquals([0, 0, 1, 0, 1, 0, 0, 0], $bits->toArray());
+ }
+
+ ///
+ /// BaseVector overrides
+ ///
+
+ public function testSet () : void {
+ $bits = BitsVector::new(4);
+ $bits->set(2, 1);
+
+ $this->assertEquals([0, 0, 1, 0], $bits->toArray());
+ }
+
+ public function testContains () : void {
+ $bits = BitsVector::new(4);
+
+ $this->assertFalse($bits->contains(1));
+ }
+
+ public function testPush () : void {
+ $bits = BitsVector::new(4);
+ $bits->push(1);
+
+ $this->assertEquals([0, 0, 0, 0, 1], $bits->toArray());
+
+ }
+
+ public function testAppend () : void {
+ $bits = BitsVector::new(4);
+
+ $bits->append([1, 1]);
+ $this->assertEquals([0, 0, 0, 0, 1, 1], $bits->toArray());
+ }
+
+ public function testUpdate () : void {
+ $bits = BitsVector::new(4);
+
+ $bits->update([1, 0, 1, 0]); // 0 already exists, we'll add ONE 1
+ $this->assertEquals([0, 0, 0, 0, 1], $bits->toArray());
+ }
+
+ public function testOffsetSet () : void {
+ $bits = BitsVector::new(4);
+ $bits[2] = 1;
+
+ $this->assertEquals([0, 0, 1, 0], $bits->toArray());
+ }
+
+}
diff --git a/tests/Collections/VectorTest.php b/tests/Collections/VectorTest.php
index 86d3370..2720ace 100644
--- a/tests/Collections/VectorTest.php
+++ b/tests/Collections/VectorTest.php
@@ -1,281 +1,299 @@
<?php
declare(strict_types=1);
namespace Keruald\OmniTools\Tests\Collections;
use Keruald\OmniTools\Collections\Vector;
use PHPUnit\Framework\TestCase;
use InvalidArgumentException;
use IteratorAggregate;
use Traversable;
+/**
+ * @covers \Keruald\OmniTools\Collections\Vector
+ * @covers \Keruald\OmniTools\Collections\BaseVector
+ */
class VectorTest extends TestCase {
private Vector $vector;
protected function setUp () : void {
$this->vector = new Vector([1, 2, 3, 4, 5]);
}
public function testConstructorWithIterable () : void {
$iterable = new class implements IteratorAggregate {
public function getIterator () : Traversable {
yield 42;
yield 100;
}
};
$vector = new Vector($iterable);
$this->assertEquals([42, 100], $vector->toArray());
}
public function testFrom () : void {
$this->assertEquals([42, 100], Vector::from([42, 100])->toArray());
}
public function testGet () : void {
$vector = new Vector(["a", "b", "c"]);
$this->assertEquals("b", $vector->get(1));
}
public function testGetOverflow () : void {
$this->expectException(InvalidArgumentException::class);
$this->vector->get(800);
}
public function testGetOr () : void {
$vector = new Vector(["a", "b", "c"]);
$this->assertEquals("X", $vector->getOr(800, "X"));
}
public function testSet () : void {
$vector = new Vector(["a", "b", "c"]);
$vector->set(1, "x"); // should replace "b"
$this->assertEquals(["a", "x", "c"], $vector->toArray());
}
public function testContains () : void {
$this->assertTrue($this->vector->contains(2));
$this->assertFalse($this->vector->contains(666));
}
public function testCount () : void {
$this->assertEquals(5, $this->vector->count());
$this->assertEquals(0, (new Vector)->count());
}
public function testClear () : void {
$this->vector->clear();
$this->assertEquals(0, $this->vector->count());
}
public function testIsEmpty () : void {
$this->vector->clear();
$this->assertTrue($this->vector->isEmpty());
}
public function testPush () : void {
$this->vector->push(6);
$this->assertEquals([1, 2, 3, 4, 5, 6], $this->vector->toArray());
}
public function testAppend () : void {
$this->vector->append([6, 7, 8]);
$this->assertEquals([1, 2, 3, 4, 5, 6, 7 ,8], $this->vector->toArray());
}
public function testUpdate () : void {
$this->vector->update([5, 5, 5, 6, 7, 8]); // 5 already exists
$this->assertEquals([1, 2, 3, 4, 5, 6, 7 ,8], $this->vector->toArray());
}
public function testMap () : void {
$actual = $this->vector
->map(function ($x) { return $x * $x; })
->toArray();
$this->assertEquals([1, 4, 9, 16, 25], $actual);
}
public function testMapKeys () : void {
$vector = new Vector(["foo", "bar", "quux", "xizzy"]);
$filter = function ($key) {
return 0; // Let's collapse our array
};
$actual = $vector->mapKeys($filter)->toArray();
$this->assertEquals(["xizzy"], $actual);
}
public function testFlatMap () : void {
$expected = [
// Squares and cubes
1, 1,
4, 8,
9, 27,
16, 64,
25, 125
];
$callback = function ($n) {
yield $n * $n;
yield $n * $n * $n;
};
$actual = $this->vector->flatMap($callback)->toArray();
$this->assertEquals($expected, $actual);
}
public function testFlatMapWithKeyValueCallback() : void {
$vector = new Vector(["foo", "bar", "quux", "xizzy"]);
$callback = function (int $key, string $value) {
yield "$key::$value";
yield "$value ($key)";
};
$expected = [
"0::foo",
"foo (0)",
"1::bar",
"bar (1)",
"2::quux",
"quux (2)",
"3::xizzy",
"xizzy (3)",
];
$actual = $vector->flatMap($callback)->toArray();
$this->assertEquals($expected, $actual);
}
public function testFlatMapWithCallbackWithoutArgument() : void {
$this->expectException(InvalidArgumentException::class);
$callback = function () {};
$this->vector->flatMap($callback);
}
public function testFilter () : void {
$vector = new Vector(["foo", "bar", "quux", "xizzy"]);
$filter = function ($item) {
return strlen($item) === 3; // Let's keep 3-letters words
};
$actual = $vector->filter($filter)->toArray();
$this->assertEquals(["foo", "bar"], $actual);
}
public function testFilterWithBadCallback () : void {
$this->expectException(InvalidArgumentException::class);
$badFilter = function () {};
$this->vector->filter($badFilter);
}
public function testFilterKeys () : void {
$filter = function ($key) {
return $key % 2 === 0; // Let's keep even indices
};
$actual = $this->vector
->filterKeys($filter)
->toArray();
$this->assertEquals([0, 2, 4], array_keys($actual));
}
+ public function testChunk () : void {
+ $vector = new Vector([1, 2, 3, 4, 5, 6]);
+
+ $this->assertEquals(
+ [[1, 2], [3, 4], [5, 6]],
+ $vector->chunk(2)->toArray()
+ );
+ }
+
+ public function testSlice () : void {
+ $actual = $this->vector->slice(2, 3);
+ $this->assertEquals([3, 4, 5], $actual->toArray());
+ }
+
public function testImplode() : void {
$actual = (new Vector(["a", "b", "c"]))
->implode(".")
->__toString();
$this->assertEquals("a.b.c", $actual);
}
public function testImplodeWithoutDelimiter() : void {
$actual = (new Vector(["a", "b", "c"]))
->implode("")
->__toString();
$this->assertEquals("abc", $actual);
}
public function testExplode() : void {
$actual = Vector::explode(".", "a.b.c");
$this->assertEquals(["a", "b", "c"], $actual->toArray());
}
public function testExplodeWithoutDelimiter() : void {
$actual = Vector::explode("", "a.b.c");
$this->assertEquals(["a.b.c"], $actual->toArray());
}
///
/// ArrayAccess
///
public function testArrayAccessFailsWithStringKey () : void {
$this->expectException(InvalidArgumentException::class);
$this->vector["foo"];
}
public function testOffsetExists () : void {
$this->assertTrue(isset($this->vector[0]));
$this->assertFalse(isset($this->vector[8]));
}
public function testOffsetSetWithoutOffset () : void {
$this->vector[] = 6;
$this->assertEquals(6, $this->vector[5]);
}
public function testOffsetSet () : void {
$this->vector[0] = 9;
$this->assertEquals(9, $this->vector[0]);
}
public function testOffsetUnset () : void {
unset($this->vector[2]);
$expected = [
0 => 1,
1 => 2,
// vector[2] has been unset
3 => 4,
4 => 5,
];
$this->assertEquals($expected, $this->vector->toArray());
}
///
/// IteratorAggregate
///
public function testGetIterator () : void {
$this->assertEquals([1, 2, 3, 4, 5], iterator_to_array($this->vector));
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Mar 7, 02:01 (23 h, 27 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3492529
Default Alt Text
(28 KB)
Attached To
Mode
rKOT Keruald OmniTools
Attached
Detach File
Event Timeline
Log In to Comment