import numpy as np
import math
from numpy.lib.stride_tricks import sliding_window_view

class AstraByte:
    def __init__(self):
        self.constellation_map = {}
        self.reverse_constellation_map = {}

    def compress(self, data):
        data_indices = self._form_constellations(data)
        compressed_data = self._cosmic_folding(data_indices)
        return np.array(compressed_data, dtype=np.uint32).tobytes()

    def decompress(self, compressed_data):
        unfolded_data = self._unfold_cosmos(compressed_data)
        data = self._reform_data(unfolded_data)
        return data

    def _form_constellations(self, data):
        data_indices = []
        for i in range(0, len(data), 2):
            constellation = data[i:i+2]
            if constellation not in self.constellation_map:
                self.constellation_map[constellation] = len(self.constellation_map)
                self.reverse_constellation_map[len(self.constellation_map) - 1] = constellation
            data_indices.append(self.constellation_map[constellation])
        return data_indices

    def _cosmic_folding(self, data):
        data = self._pad_data(data)
        compressed_data = np.array(data).reshape(int(np.sqrt(len(data))), int(np.sqrt(len(data))))
        while compressed_data.shape[0] > 1:
            new_data = self._fold_matrix(compressed_data)
            compressed_data = new_data
        return compressed_data.flatten().tolist()

    def _unfold_cosmos(self, compressed_data):
        compressed_data = np.frombuffer(compressed_data, dtype=np.uint32)
        size = int(np.sqrt(len(self.constellation_map)))
        unfolded_data = np.full((size, size), compressed_data)
        while unfolded_data.shape[0] < len(self.constellation_map):
            new_data = self._unfold_matrix(unfolded_data)
            unfolded_data = new_data
        return unfolded_data

    def _reform_data(self, unfolded_data):
        data = ''
        for index in unfolded_data.flatten().tolist():
            if index in self.reverse_constellation_map:
                data += self.reverse_constellation_map[index]
        return data

    def _pad_data(self, data):
        if len(data) % 2 != 0:
            data.append(0)
        next_square = math.ceil(math.sqrt(len(data))) ** 2
        padding_length = next_square - len(data)
        for _ in range(padding_length):
            data.append(0)
        return data

    def _fold_matrix(self, compressed_data):
        size = compressed_data.shape[0]
        new_size = size // 2
        strided = sli