Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
372 changes: 34 additions & 338 deletions README.md

Large diffs are not rendered by default.

Binary file added b-tree.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
83 changes: 60 additions & 23 deletions persistent_data_structures/base_persistent.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,81 @@
from copy import deepcopy
from typing import Optional, Any


class NodeState:
"""Класс, представляющий состояние узла."""
def __init__(self, data: Optional['NodeState'] = None):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

С питона 3.10 вместо data: Optional['NodeState'] = None можно писать:
data: NodeState | None = None

self.data = data
self.next_node: Optional['NodeState'] = None


class Node:
"""Класс узла для хранения состояния в B-дереве."""
def __init__(self, state: NodeState | None) -> None:
"""
Инициализирует узел с заданным состоянием.

:param state: Состояние узла.
"""
self.state: NodeState | None = state
self.children: dict[int, Node] = {}


class BasePersistent:
"""Базовый класс для персистентных стркутур данных.

Каждая персистентная структура будет хранить в себе историю изменений в виде словаря с ключами
версиями и значениями - состояниями. Также персистентная структура будет хранить номер ткущей
и номер последней версии.
"""
def __init__(self, initial_state=None) -> None:
"""Инициализирует персистентную структуру данных.
"""Базовый класс для персистентных структур данных с использованием B-дерева."""

def __init__(self, initial_state: Optional[NodeState] = None) -> None:
"""
Инициализирует персистентную структуру данных.

:param initial_state: Начальное состояние персистентной структуры данных.
"""
self._history = {0: initial_state}
self._current_state = 0
self._last_state = 0
self.root: Node = Node(initial_state)
self._current_version: int = 0
self._last_version: int = 0
self._version_map: dict[int, Node] = {0: self.root}

def get_version(self, version):
"""Возвращает состояние персистентной структуры данных на указанной версии.
def get_version(self, version: int) -> dict[Any, Any]:
"""
Возвращает состояние персистентной структуры данных на указанной версии.

:param version: Номер версии.
:return: Состояние персистентной структуры данных на указанной версии.
:raises ValueError: Если указанная версия не существует.
"""
if version < 0 or version >= len(self._history):
if version not in self._version_map:
raise ValueError(f'Version "{version}" does not exist')
return self._history[version]
return self._version_map[version].state

def update_version(self, version):
"""Обновляет текущую версию персистентной структуры данных до указанной.
def set_version(self, version: int) -> None:
"""
Обновляет текущую версию персистентной структуры данных до указанной.

:param version: Номер версии.
:raises ValueError: Если указанная версия не существует.
"""
if version < 0 or version >= len(self._history):
if version not in self._version_map:
raise ValueError(f'Version "{version}" does not exist')
self._current_state = version
self._current_version = version
self.root = self._version_map[version]

def _create_new_state(self) -> None:
"""Создает новую версию."""
self._last_state += 1
self._history[self._last_state] = deepcopy(self._history[self._current_state])
self._current_state = self._last_state
"""
Создает новую версию персистентной структуры данных с
минимальным дублированием данных.

Этот метод копирует текущее состояние структуры данных и создает
новую версию, добавляя ее в карту версий.
Дублирование данных минимизируется путем использования глубокого
копирования состояния узла.

:raises ValueError: Если текущая версия не существует в карте версий.
"""
self._last_version += 1
parent_node = self._version_map[self._current_version]
new_state = deepcopy(parent_node.state)
new_node = Node(new_state)
parent_node.children[self._last_version] = new_node
self._version_map[self._last_version] = new_node
self._current_version = self._last_version
self.root = new_node
120 changes: 68 additions & 52 deletions persistent_data_structures/persistent_array.py
Original file line number Diff line number Diff line change
@@ -1,126 +1,142 @@
import numpy as np

from persistent_data_structures.base_persistent import BasePersistent
from base_persistent import BasePersistent


class PersistentArray(BasePersistent):
"""Персистентный массив.
Класс PersistentArray реализует неизменяемый массив с
возможностью хранения нескольких версий, где каждая
версия является изменением предыдущей.
"""
"""Персистентный массив с использованием B-дерева."""

def __init__(self, size: int = 1024, default_value: int = 0) -> None:
"""Инициализирует новый массив с несколькими версиями.
"""
Инициализирует новый массив с несколькими версиями.

Создается первая версия массива, которая состоит из элементов,
равных default_value.

:param size: Начальный размер массива (по умолчанию 1024).
:param default_value: Значение по умолчанию для элементов массива (по умолчанию 0).
"""
self.size = size
self.default_value = default_value
initial_state = np.full(size, default_value)
self.size: int = size
self.default_value: int = default_value
initial_state: np.ndarray = np.full(size, default_value)
super().__init__(initial_state)

def __getitem__(self, index: int) -> any:
"""Получение значения из текущей версии массива по индексу.
"""
Получение значения из текущей версии массива по индексу.

:param index: Индекс элемента в текущей версии массива.
:return: Значение элемента в текущей версии массива по заданному индексу.
:raises ValueError: Если индекс выходит за пределы допустимого диапазона.
"""
if index < 0 or index >= self.size:
raise ValueError("Invalid index")
return self._history[self._current_state][index]
return self.root.state[index]

def get(self, version: int, index: int) -> any:
"""Получение значения элемента для определенной версии массива по индексу.
def __setitem__(self, index: int, value: any) -> None:
"""
Обновление значения элемента в новой версии массива.

:param version: Номер версии, из которой нужно получить элемент.
:param index: Индекс элемента в указанной версии массива.
:return: Значение элемента в указанной версии массива по заданному индексу.
:raises ValueError: Если версия или индекс выходят за пределы допустимого диапазона.
Обновляет значение элемента из текущей версии массива по индексу
и помещает получившийся массив в новую версию.

:param index: Индекс элемента, который необходимо обновить.
:param value: Новое значение для обновляемого элемента.
:raises ValueError: Если индекс выходит за пределы допустимого диапазона.
"""
if version > self._current_state or version < 0:
raise ValueError(f'Version "{version}" does not exist')
if index < 0 or index >= self.size:
raise ValueError("Invalid index")
return self._history[version][index]
self._create_new_state()
self.root.state[index] = value

def add(self, value: any) -> None:
"""Добавление нового элемента в конец массива в новую версию.
"""
Добавление нового элемента в конец массива в новую версию.

:param value (int): Значение нового элемента, который добавляется в массив.
:param value: Значение нового элемента, который добавляется в массив.
"""
self._create_new_state()
self._history[self._last_state] = np.append(self._history[self._last_state], value)
self.root.state = np.append(self.root.state, value)
self.size += 1

def pop(self, index: int) -> any:
"""Удаление элемента в новой версии массива и возвращение его значения.
"""
Удаление элемента в новой версии массива и возвращение его значения.

:param index (int): Индекс элемента, который необходимо удалить.
:return int: Значение удаленного элемента.
:param index: Индекс элемента, который необходимо удалить.
:return: Значение удаленного элемента.
:raises ValueError: Если индекс выходит за пределы допустимого диапазона.
"""
if index < 0 or index >= self.size:
raise ValueError("Invalid index")
removed_element = self._history[self._current_state][index]
self._create_new_state()
self._history[self._last_state] = np.delete(self._history[self._last_state], index)
removed_element = self.root.state[index]
self.root.state = np.delete(self.root.state, index)
self.size -= 1
return removed_element

def __setitem__(self, index: int, value: any) -> None:
"""Обновление значения элемента в новую версии массива.

Обновляет значение элемента из текущей версии массива по индексу
и помещает получившийся массив в новую версию.
:param index: Индекс элемента, который необходимо обновить.
:param value: Новое значение для обновляемого элемента.
:raises ValueError: Если индекс выходит за пределы допустимого диапазона.
"""
if index < 0 or index >= self.size:
raise ValueError("Invalid index")
self._create_new_state()
self._history[self._last_state][index] = value

def insert(self, index: int, value: any) -> None:
"""Вставка нового элемента в массив в указанную позицию в новой версии.
"""
Вставка нового элемента в массив в указанную позицию в новой версии.

Вставляет новый элемент в указанную позицию в текущей версии массива и помещает
получившийся массив в новую версию.
:param index: Позиция, в которую нужно вставить новый элемент.
:param value: Значение нового элемента, который нужно вставить.
:raises ValueError: Если индекс выходит за пределы допустимого диапазона.
"""
if index < 0 or index > self.size:
raise ValueError("Invalid index")
self._create_new_state()
self._history[self._last_state] = np.insert(self._history[self._last_state], index, value)
self.root.state = np.insert(self.root.state, index, value)
self.size += 1

def get(self, version: int, index: int) -> any:
"""
Получение значения элемента для определенной версии массива по индексу.

:param version: Номер версии, из которой нужно получить элемент.
:param index: Индекс элемента в указанной версии массива.
:return: Значение элемента в указанной версии массива по заданному индексу.
:raises ValueError: Если версия или индекс выходят за пределы допустимого диапазона.
"""
if version < 0 or version > self._last_version:
raise ValueError(f'Version "{version}" does not exist')
state = self.get_version(version)
if index < 0 or index >= len(state):
raise ValueError("Invalid index")
return state[index]

def remove(self, index: int) -> None:
"""Удаление элемента в новой версии массива по индексу.
"""
Удаление элемента в новой версии массива по индексу.

Удаляет элемент из текущей версии массива по индексу и помещает результат в новую версию.

:param index: Индекс элемента, который необходимо удалить.
:raises ValueError: Если индекс выходит за пределы допустимого диапазона.
"""
if index < 0 or index >= self.size:
raise ValueError("Invalid index")
self.pop(index)

def get_version_state(self, version: int) -> np.ndarray:
"""
Получение состояния массива для указанной версии.

:param version: Номер версии.
:return: Состояние массива для указанной версии.
"""
return self.get_version(version)

def get_size(self) -> int:
"""Получение текущего размера массива.
"""
Получение текущего размера массива.

:return: Количество элементов в текущей версии массива.
"""
return self.size

def check_is_empty(self):
"""Проверка, является ли массив пустым в текущей версии.
def check_is_empty(self) -> bool:
"""
Проверка, является ли массив пустым в текущей версии.

:return: True, если массив пуст, иначе False.
"""
Expand Down
Loading
Loading