Page MenuHomeDevCentral

No OneTemporary

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

Mime Type
text/x-diff
Expires
Sat, Mar 7, 02:01 (1 d, 1 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3492529
Default Alt Text
(28 KB)

Event Timeline