Загрузка данных


class Node:
    def __init__(self, val):
        self.val = val
        self.next = None
        self.prev = None




class Double_Linked_List:
    def __init__(self, *lst):
        self.head = self.tail = None
        self.size = 0


        for node in lst:
            self.append(node)
      
      
    def __len__(self):
        return self.size


    def __str__(self):
        if not self.head:
            return "empty"
        cur = self.head
        res = ""
        while cur:
            if not cur.next:
                res +=f"{cur.val}"
                break
            res +=f"{cur.val}<>"
            cur = cur.next
        return res
                           


    
    def __get_item__(self, idx):
        return self._node_at(idx)


    def __getitem__(self, key):
        return self.__get_node(key).val


    def __setitem__(self, idx, value):
        self._node_at(idx).val = value


    def append(self, val):
        new_node = Node(val)
        self.size +=1
        if not self.head:
            self.head= self.tail = new_node
            new_node.prev =self.tail
            self.tail =new_node




    def __get_node(self, idx):
        if idx <0:
            idx += self.size
        if idx > self.size -1 or idx <0:
            raise IndexError('index out of range')

        if idx > self.size // 2:
            cur = self.tail
            cur_idx = self.size - 1
            while cur_idx != idx:
                cur = cur.prev
                cur_idx -= 1
            return cur
        else:
            cur = self.head
            cur_idx = 0
            while cur_idx != idx:
                cur = cur.next
                cur_idx += 1
            return cur



    def insert(self, idx, val):
        if idx < 0 or idx > self.length:
            return False             
        new_node = Node(val)
        if idx == 0:
            new_node.next = self.head
            self.head = new_node
        else:
            current = self.head
            for _ in range(idx - 1):
                current = current.next
            new_node.next = current.next
            current.next = new_node
            
        self.length += 1
        return True
      
      


    def remove(self, val):
        current = self.head
        while current is not None:
            if current.val == val:
                if current == self.head:
                    self.head = current.next
                    if self.head is not None:
                        self.head.prev = None
                else:
                    current.prev.next = current.next
                    if current.next is not None:
                        current.next.prev = current.prev
                self.size -= 1
                return True 
            current = current.next
        return False  

        


    def pop(self, idx=-1):
        if idx == 0:
           tmp = self.head.val
           self.head =self.head.next
           self.head.prev = None
        else:
            target =self.__get_node(idx - 1)
            tmp = target.next.val
            target.next = target.next.next
            target.next.prev = target
            self.size -=1
            return tmp 
    
 


    def swap(self, idx1, idx2):
        if idx1 == idx2:
            return
        node1 = self.__get_node(idx1)
        node2 = self.__get_node(idx2)
        node1.val, node2.val = node2.val, node1.val
 
      
      


    def reverse(self):
        if self.head is None or self.head.next is None:
            return
        current = self.head
        tmp = None
        while current is not None:
            tmp = current.prev
            current.prev = current.next
            current.next = tmp
            current = current.prev
        if tmp is not None:
            self.head = tmp.prev