public function push(OperationInterface $newOperation)
 {
     $lastOperation = ($index = count($this->operations)) > 0 ? $this->operations[$index - 1] : null;
     if ($lastOperation instanceof OperationInterface) {
         if ($lastOperation->getType() === OperationInterface::TYPE_DELETE && $newOperation->getType() === OperationInterface::TYPE_DELETE) {
             $this->operations->set($index - 1, new DeleteOperation($lastOperation->getValue() + $newOperation->getValue()));
             return $this;
         }
         if ($lastOperation->getType() === OperationInterface::TYPE_DELETE && $newOperation->getType() === OperationInterface::TYPE_INSERT) {
             $index -= 1;
             if (!isset($this->operations[$index - 1])) {
                 $this->operations->unshift($newOperation);
                 return $this;
             }
             $lastOperation = $this->operations[$index - 1];
         }
         if ($lastOperation->getAttributes() === $newOperation->getAttributes()) {
             if ($newOperation->getType() === OperationInterface::TYPE_INSERT && is_string($newOperation->getValue()) && $lastOperation->getType() === OperationInterface::TYPE_INSERT && is_string($lastOperation->getValue())) {
                 $this->operations[$index - 1] = new InsertOperation($lastOperation->getValue() . $newOperation->getValue(), $newOperation->getAttributes());
                 return $this;
             } elseif ($newOperation->getType() === OperationInterface::TYPE_RETAIN && $lastOperation->getType() === OperationInterface::TYPE_RETAIN) {
                 $this->operations[$index - 1] = new RetainOperation($lastOperation->getValue() + $newOperation->getValue(), $newOperation->getAttributes());
                 return $this;
             }
         }
     }
     if ($index === count($this->operations)) {
         $this->operations->add($newOperation);
     } else {
         $this->operations->splice($index, 0, $newOperation);
     }
     return $this;
 }
 private function calculateLength(OperationInterface $operation)
 {
     if ($operation->getType() === OperationInterface::TYPE_DELETE || $operation->getType() === OperationInterface::TYPE_RETAIN) {
         return $operation->getValue();
     }
     return $operation->getType() == OperationInterface::TYPE_INSERT && is_string($operation->getValue()) ? mb_strlen($operation->getValue()) : 1;
 }