MBHFramework/structures

View on GitHub
Mbh/Collection/Traits/Sequenceable/Arrayed.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php namespace Mbh\Collection\Traits\Sequenceable;

use Mbh\Collection\Interfaces\Collection as CollectionInterface;
use Mbh\Collection\Interfaces\Sequenceable as SequenceableInterface;
use Mbh\Collection\FixedArray;
use Mbh\Collection\CallbackHeap;
use Mbh\Iterator\SliceIterator;
use Mbh\Iterator\ConcatIterator;
use Mbh\Collection\Traits\Builder;
use SplFixedArray;
use SplHeap;
use SplStack;
use LimitIterator;
use Iterator;
use ArrayAccess;
use Countable;
use CallbackFilterIterator;
use JsonSerializable;
use RuntimeException;
use Traversable;
use ReflectionClass;
use UnderflowException;
use OutOfRangeException;

/**
 * MBHFramework
 *
 * @link      https://github.com/MBHFramework/mbh-framework
 * @copyright Copyright (c) 2017 Ulises Jeremias Cornejo Fandos
 * @license   https://github.com/MBHFramework/mbh-framework/blob/master/LICENSE (MIT License)
 */

trait Arrayed
{
    use Arrayed\Countable;
    use Arrayed\ArrayAccess;
    use Arrayed\Iterator;
    use Builder;

    protected $sfa = null;

    /**
     * Create an fixed array
     *
     * @param Traversable $array data
     */
    protected function __construct(Traversable $array)
    {
        $this->sfa = $array;
    }

    /**
     * @inheritDoc
     */
    public function first()
    {
        $this->emptyGuard(__METHOD__);
        return $this[0];
    }

    /**
     * @inheritDoc
     */
    public function get(int $index)
    {
        if (!$this->validIndex($index)) {
            throw new OutOfRangeException();
        }

        return $this[$index];
    }

    /**
     * @inheritDoc
     */
    public function insert(int $index, ...$values)
    {
        if (!$this->validIndex($index) && $index !== $this->count()) {
            throw new OutOfRangeException();
        }

        $splice = $this->splice($index, 0, $values);
        $this->clear();

        $this->pushAll($splice);
    }

    /**
     * @inheritDoc
     */
    public function last()
    {
        $this->emptyGuard(__METHOD__);
        return $this[$this->count() - 1];
    }

    /**
     * Converts negative or large rotations into the minimum positive number
     * of rotations required to rotate the sequence by a given $r.
     */
    private function normalizeRotations(int $r)
    {
        $n = $this->count();

        if ($n < 2) {
            return 0;
        }

        if ($r < 0) {
            return $n - (abs($r) % $n);
        }

        return $r % $n;
    }

    /**
     * @inheritDoc
     */
    public function pop()
    {
        $this->emptyGuard(__METHOD__);
        $value = $this->last();
        $count = $this->count();
        unset($this[--$count]);
        $this->setSize($count);

        $this->checkCapacity();

        return $value;
    }

    /**
     * Pushes all values of either an array or traversable object.
     */
    private function pushAll(...$args)
    {
        $size = $this->getSize();

        foreach ($args as &$values) {
            $this->setSize($size + count($values));

            foreach ($values as $value) {
                $this[$size++] = $value;
            }
        }

        $this->checkCapacity();
    }

    /**
     * @inheritDoc
     */
    public function push(...$values)
    {
        $this->pushAll($values);
    }

    /**
     * @inheritDoc
     */
    public function remove(int $index)
    {
        if (!$this->validIndex($index)) {
            throw new OutOfRangeException();
        }

        $value = $this[$index];
        $splice = $this->splice($index + 1, 1, null);
        $this->clear();

        $this->pushAll($splice);
        return $value;
    }

    /**
     * @inheritDoc
     */
    public function set(int $index, $value)
    {
        if (!$this->validIndex($index)) {
            throw new OutOfRangeException();
        }

        $this->sfa->offsetSet($index, $value);
    }

    /**
     * @inheritDoc
     */
    public function shift()
    {
        $this->emptyGuard(__METHOD__);
        $value = $this->first();
        unset($this[0]);

        $this->checkCapacity();

        return $value;
    }

    /**
     * @inheritDoc
     */
    public function toArray(): array
    {
        return $this->sfa->toArray();
    }

    /**
     * @inheritDoc
     */
    public function unshift(...$values)
    {
        $this->insert(0, ...$values);

        return $this->count();
    }

    /**
     * @inheritDoc
     */
    protected function validIndex(int $index)
    {
        return $index >= 0 && $index < $this->count();
    }

    /**
     * @inheritDoc
     */
    public function clear()
    {
        $this->sfa->setSize(0);
        $this->capacity = self::MIN_CAPACITY;
    }

    protected function getValues(): Traversable
    {
        return $this->sfa;
    }

    protected function setValues(Traversable $traversable)
    {
        $this->clear();
        $this->pushAll($traversable);
    }

    /**
     * Gets the size of the array.
     *
     * @return int
     */
    protected function getSize(): int
    {
        return $this->sfa->getSize();
    }

    /**
     * Change the size of an array to the new size of size.
     * If size is less than the current array size, any values after the
     * new size will be discarded. If size is greater than the current
     * array size, the array will be padded with NULL values.
     *
     * @param int $size The new array size. This should be a value between 0
     * and PHP_INT_MAX.
     * @return bool Returns TRUE on success or FALSE on failure.
     */
    protected function setSize(int $size): bool
    {
        return $this->sfa->setSize($size);
    }

    public function __clone()
    {
        $this->sfa = SplFixedArray::fromArray($this->toArray());
    }

    abstract protected function checkCapacity();

    abstract protected function emptyGuard($method);

    abstract public function isEmpty(): bool;

    abstract public function splice(int $begin = 0, int $length = null, $replacement = []);
}