def append(self, item):
        if self.storage.length == self.capacity:
            if self.wildcard == self.storage.tail:
                self.storage.remove_from_head()
                self.storage.add_to_head(item)
                self.wildcard = self.storage.head
            else:
                value = ListNode(item)
                value.prev = self.wildcard.next.prev
                value.next = self.wildcard.next.next
                if self.wildcard.prev:
                    self.wildcard.prev = value
                if self.wildcard.next:
                    self.wildcard.next = value
                    if self.wildcard.next.next:
                        self.wildcard.next.next.prev = value
                if value and value.next is None:
                    self.storage.tail = value
                    self.wildcard = self.wildcard.next
                else:
                    self.wildcard = self.wildcard.next


            # self.wildcard = self.storage.tail
            # if self.wildcard.next is None:
            #     wildcard = self.storage.head
            # else:
            #     wildcard = self.wildcard.next
            # wildcard.prev.next = item
            # wildcard.next.prev = item
            return
        self.storage.add_to_tail(item)
        self.wildcard = self.storage.tail
 def enqueue(self, value):
     if self.last is None:
         self.head = ListNode(value)
         self.last = self.head
         self.size += 1
     else:
         self.last.next = ListNode(value)
         self.last.next.prev = self.last
         self.last = self.last.next
         self.size += 1
Example #3
0
 def enqueue(self, value):
     new_node = ListNode(value)
     self.size += 1
     if not self.head and not self.tail:
         self.head = new_node
         self.tail = new_node
     else:
         new_node.prev = self.tail
         self.tail.next = new_node
         self.tail = new_node
    def test_node_delete(self):
        node_1 = ListNode(3)
        node_2 = ListNode(4)
        node_3 = ListNode(5)

        node_1.next = node_2
        node_2.next = node_3
        node_2.prev = node_1
        node_3.prev = node_2

        self.dll.delete(node_2)

        self.assertEqual(node_1.next, node_3)
        self.assertEqual(node_3.prev, node_1)
Example #5
0
 def set(self, key, value):
     if key not in self.storagedict.keys():
         if len(self) == self.limit:
             dropme = self.tail
             print("dropping ", dropme)
             self.storagedict = {k:v for k, v in self.storagedict.items() if v != dropme}
             self.remove_from_tail()
         node = ListNode(value)
         node.id = key
         print("setting ", node.id)
     else:
         print(key, "already in storage")
         node = self.storagedict[key]
     self.move_to_front(node)
     self.storagedict[key] = node
Example #6
0
    def set(self, key, value):
        """
        Adds the given key-value pair to the cache. The newly-
        added pair should be considered the most-recently used
        entry in the cache. If the cache is already at max capacity
        before this entry is added, then the oldest entry in the
        cache needs to be removed to make room. Additionally, in the
        case that the key already exists in the cache, we simply
        want to overwrite the old value associated with the key with
        the newly-specified value.
        """
        if key in self.memo:
            node = self.memo[key]
            node.value = (key, value)
            self.queue.move_to_end(node)
            return

        node = ListNode((key, value))

        self.memo[key] = node
        self.queue.move_to_end(node)
        self.queue.length += 1

        if self.queue.length > self.limit:
            value_ = self.queue.remove_from_head()
            self.memo.pop(value_[0], None)
            self.queue.length -= 1
Example #7
0
 def enqueue(self, value):
     self.length += 1
     if not self.head and not self.tail:
         self.head = self.tail = ListNode(value)
     else:
         self.tail.insert_after(value)
         self.tail = self.tail.next
Example #8
0
 def push(self, value):
     self.height += 1
     if self.top:
         self.top.insert_after(value)
         self.top = self.top.next
     else:
         self.top = ListNode(value)
Example #9
0
 def set(self, key, value):
     if key in self.storage:
         self.storage[key] = value
         #move the node to the front
         current_node = self.dlist.head
         while current_node != None:
             if current_node.value == key:
                 break
             else:
                 current_node = current_node.next
     else:
         #checks if cache is empty
         if self.current == 0:
             #creates a new doubly linked list
             self.dlist = DoublyLinkedList(ListNode(key))
             #adds the key value pair to the dictionary
             self.storage[key] = value
             self.current += 1
         #check if cache is full
         elif self.current == self.limit:
             #removes tail from the doubly linked list
             removedKey = self.dlist.remove_from_tail()
             #removes key and value from dictionary
             del self.storage[removedKey]
             #adds key to the front of the doubly linked list
             self.dlist.add_to_head(key)
             #adds the key value pair to the dictionary
             self.storage[key] = value
         else:
             #adds key to the front of the doubly linked list
             self.dlist.add_to_head(key)
             #adds the key value pair to the dictionary
             self.storage[key] = value
             self.current += 1
Example #10
0
def add_n_elements_delete_half_random(n):
    # Don't time initialization
    big_arr = array(n)
    small_arr = array(1)
    node = ListNode(1)
    dll = DoublyLinkedList(node)

    # Experiment with this.
    # How does higher or lower change things?
    divisor = 10

    # Array size N
    start = timer()
    for i in range(n):
        array_append(big_arr, random.randrange(n / divisor))

    for i in range(int(n / 2)):
        array_remove(big_arr, random.randrange(n / divisor))

    time = timer() - start
    print("Size N Array: Appended " + str(n) +
          " elements and removed half in " + str(time))

    # Array size 1 (To start)
    start = timer()
    for i in range(n):
        array_append(small_arr, random.randrange(int(n / divisor)))

    for i in range(int(n / 2)):
        array_remove(small_arr, random.randrange(int(n / divisor)))

    time = timer() - start
    print("Size 1 Array: Appended " + str(n) +
          " elements and removed half in " + str(time))

    # Linked List - Head
    start = timer()
    for i in range(n):
        dll.add_to_head(random.randrange(int(n / divisor)))

    for i in range(int(n / 2)):
        dll.find_and_delete(random.randrange(int(n / divisor)))

    time = timer() - start
    print("Linked Head: Added " + str(n) + " elements and removed half in " +
          str(time))

    # Linked List - Tail
    start = timer()
    for i in range(n):
        dll.add_to_tail(random.randrange(int(n / divisor)))

    for i in range(int(n / 2)):
        dll.find_and_delete(random.randrange(int(n / divisor)))

    time = timer() - start
    print("Linked Tail: Added " + str(n) + " elements and removed half in " +
          str(time))
 def get(self, key):
     try:
         value = self.dict.pop(key)
         node = ListNode(key)
         self.storage.move_to_end(node)
         self.dict[key] = value
         return value
     except KeyError:
         return
Example #12
0
 def set(self, key, value):
     if key in self.hash:
         self.storage.move_to_front(self.hash[key])
         self.get(key)
     if self.size < self.limit:
         self.storage.add_to_head(ListNode(value))
         self.size += 1
         self.hash[key] = ListNode(value)
     if self.size >= self.limit:
         temp = {
             key: val
             for key, val in self.hash.items()
             if val.value != self.storage.tail.value.value
         }
         self.hash = temp
         del temp
         self.storage.remove_from_tail()
         self.storage.add_to_head(ListNode(value))
         self.hash[key] = ListNode(value)
Example #13
0
 def set(self, key, value):
     if key in self.storage.keys():
         self.storage[key] = ListNode(value)
     else:
         if self.cache.length < self.limit:
             self.cache.add_to_head(value)
             self.storage[key] = self.cache.head
         elif self.cache.length >= self.limit:
             self.cache.tail.value = None
             self.cache.remove_from_tail()
             self.cache.add_to_head(value)
             self.storage[key] = self.cache.head
Example #14
0
 def set(self, key, value):
     if self.storage.get(key):
         node = self.storage[key]
         node.value = (key, value)
         self.dll.move_to_front(node)
     else:
         node = ListNode((key, value))
         self.dll.add_to_head(node)
         self.storage[node.value[0]] = node
     print(self.storage)
     if len(self.storage) > self.limit:
         node = self.dll.remove_from_tail()
         print(f"In set function: {node}")
         self.storage[node.value[0]] = None
         self.dll.delete(node)
Example #15
0
 def append(self, item):
     if self.length < self.capacity:
         self.storage.add_to_tail(item)
         self.current = self.storage.tail
         self.length += 1
     elif self.current == self.storage.tail:
         self.storage.remove_from_head()
         self.storage.add_to_head(item)
         self.current = self.storage.head
     else:
         new_node = ListNode(item, self.current, self.current.next)
         self.current.next.prev = new_node
         self.current.next = new_node
         self.storage.delete(new_node.next)
         self.current = new_node
         self.storage.length += 1
Example #16
0
def add_n_elements(n):
    # Don't time initialization
    big_arr = array(n)
    small_arr = array(1)
    node = ListNode(1)
    dll = DoublyLinkedList(node)

    # Array size N
    start = timer()
    for i in range(n):
        array_append(big_arr, "value")

    time = timer() - start
    print("Size N Array: Appended " + str(n) + " elements in " + str(time))

    # Array size 1 (To start)
    start = timer()
    for i in range(n):
        array_append(small_arr, "value")

    time = timer() - start
    print("Size 1 Array: Appended " + str(n) + " elements in " + str(time))

    # Linked List - Head
    start = timer()
    for i in range(n):
        dll.add_to_head("value")

    time = timer() - start
    print("Linked Head: Added " + str(n) + " elements in " + str(time))

    # Linked List - Tail
    start = timer()
    for i in range(n):
        dll.add_to_tail("value")

    time = timer() - start
    print("Linked Tail: Added " + str(n) + " elements in " + str(time))
    def set(self, key, value):
        if len(self.cache.keys()) == 0:
            #If this is the first item in cache
            self.keyChain = DoublyLinkedList(ListNode(key))
            self.cache[key] = value
        elif key in self.cache.keys():
            #Else If this key is already in cache
            self.cache[key] = value
            current = self.keyChain.head
            while current.value != key:
                current = current.next
            self.keyChain.move_to_end(current)
        elif len(self.cache.keys()) == self.limit:
            #Else If the cache is at its limits
            del self.cache[self.keyChain.head.value]
            self.keyChain.remove_from_head()
            self.keyChain.add_to_tail(key)
            self.cache[key] = value

        else:
            #Else Neither of the above conditions are true
            #IE. Not first item in cache, key not already in cache, and the cache has not reached the limit
            self.keyChain.add_to_tail(key)
            self.cache[key] = value
 def push(self, value):
     self.storage.add_to_tail(ListNode(value))
     self.size += 1
Example #19
0
class DoublyLinkedListTests(unittest.TestCase):

    def setUp(self):

        self.node = ListNode(1)

        self.dll = DoublyLinkedList(self.node)

    def test_list_remove_from_tail(self):

        self.dll.add_to_tail(33)

        self.assertEqual(self.dll.remove_from_tail(), 33)

        self.dll.add_to_tail(68)

        self.assertEqual(self.dll.remove_from_tail(), 68)

    def test_list_remove_from_head(self):

        self.dll.add_to_head(2)

        self.assertEqual(self.dll.remove_from_head(), 2)

        self.dll.add_to_head(55)

        self.assertEqual(self.dll.remove_from_head(), 55)

    def test_list_add_to_tail(self):

        self.dll.add_to_tail(30)

        self.assertEqual(self.dll.tail.value, 30)

        self.dll.add_to_tail(20)

        self.assertEqual(self.dll.tail.value, 20)

    def test_node_delete(self):

        # node_1 = ListNode(3)

        # node_2 = ListNode(4)

        # node_3 = ListNode(5)

        # node_1.next = node_2

        # node_2.next = node_3

        # node_2.prev = node_1

        # node_3.prev = node_2

        # node_2.delete()

        # implement test for dll.delete()

        self.dll.add_to_tail(3)

        node_1 = self.dll.tail

        self.dll.add_to_tail(4)

        node_2 = self.dll.tail

        self.dll.add_to_tail(5)

        node_3 = self.dll.tail

        # test position of node_2 before deletion

        self.assertEqual(node_1.next, node_2)

        self.assertEqual(node_3.prev, node_2)

        self.dll.delete(node_2)

        self.assertEqual(node_1.next, node_3)

        self.assertEqual(node_3.prev, node_1)

    def test_node_insert_before(self):

        self.node.insert_before(0)

        self.assertEqual(self.node.prev.value, 0)

    def test_list_add_to_head(self):

        self.assertEqual(self.dll.head.value, 1)

        self.dll.add_to_head(10)

        self.assertEqual(self.dll.head.value, 10)

        self.assertEqual(self.dll.head.next.value, 1)

    def test_node_insert_after(self):

        self.node.insert_after(2)

        self.assertEqual(self.node.next.value, 2)

    def test_list_move_to_end(self):

        self.dll.add_to_head(40)

        self.assertEqual(self.dll.tail.value, 1)

        self.assertEqual(self.dll.head.value, 40)

        self.dll.move_to_end(self.dll.head)

        self.assertEqual(self.dll.tail.value, 40)

        self.assertEqual(self.dll.tail.prev.value, 1)

    def test_list_move_to_front(self):

        self.dll.add_to_tail(3)

        self.assertEqual(self.dll.head.value, 1)

        self.assertEqual(self.dll.tail.value, 3)

        self.dll.move_to_front(self.dll.tail)

        self.assertEqual(self.dll.head.value, 3)

        self.assertEqual(self.dll.head.next.value, 1)

    def test_get_max(self):

        # clear dll to pass first test

        self.dll.delete(self.dll.head)

        self.assertIsNone(self.dll.get_max())

        self.dll.add_to_tail(100)

        self.assertEqual(self.dll.get_max(), 100)

        self.dll.add_to_tail(55)

        self.assertEqual(self.dll.get_max(), 100)

        self.dll.add_to_tail(101)

        self.assertEqual(self.dll.get_max(), 101)
Example #20
0
 def push(self, value):
     self.size += 1
     return self.storage.add_to_head(ListNode(value))
Example #21
0
    def set(self, key, value):
        if not key in self.cache:
            self.cache[key] = value
            self.dll.add_to_head(key)
        else:
            self.cache[key] = value
            node = ListNode(value)
            self.dll.move_to_front(node)
        if self.dll.length > self.limit:
            del self.cache[self.dll.tail.value]
            self.dll.remove_from_tail()


#  class example

# class LRUCache:
#     """
#     Our LRUCache class keeps track of the max number of nodes it
#     can hold, the current number of nodes it is holding, a doubly-
#     linked list that holds the key-value entries in the correct
#     order, as well as a storage dict that provides fast access
#     to every node stored in the cache.
#     """
#     def __init__(self, limit=10):
#         self.limit = limit
#         self.size = 0
#         self.storage = {}
#         self.order = DoublyLinkedList()

#     def get(self, key):
#         # if key is in storage
#         if key in self.storage:
#             node = self.storage[key]
#             self.order.move_to_end(node)
#             return node.value[1]
#         else:
#             return None

#     def set(self, key):
#         # check and see if they key is in the dict
#         if key is self.storage:
#             # if it is
#             node = self.storage[key]
#             # overwrite the value
#             node.value = (key, value)
#             # move it to the end
#             self.order.move_to_end(node)
#             return

#             # check and see if cache is full
#             if self.size == self.limit:
#                 # remove oldest entry from dictionary
#                 del self.storage[self.order.head.value[0]]
#                 # and LL
#                 self.order.remove_from_head()
#                 self.size -= 1
#             # add to the LL (key, value)
#             self.order.add_to_tail((key, value))
#             # add the key and value to the dict
#             self.storage[key] = self.order.tail
#             # increment size
#             self.size += 1
Example #22
0
def create_doubly_linked_list_numbers():
    dll = DoublyLinkedList(ListNode(1))
    for i in range(2, 100000):
        dll.add_to_tail(i)
    return dll
Example #23
0
 def enqueue(self, value):
     self.size += 1
     return self.storage.add_to_tail(ListNode(value))
Example #24
0
class DoublyLinkedListTests(unittest.TestCase):
    def setUp(self):
        self.node = ListNode(1)
        self.dll = DoublyLinkedList(self.node)

    def test_list_remove_from_tail(self):
        self.dll.remove_from_tail()
        self.assertIsNone(self.dll.head)
        self.assertIsNone(self.dll.tail)
        self.assertEqual(len(self.dll), 0)

        self.dll.add_to_tail(33)
        self.assertEqual(self.dll.head.value, 33)
        self.assertEqual(self.dll.tail.value, 33)
        self.assertEqual(len(self.dll), 1)
        self.assertEqual(self.dll.remove_from_tail(), 33)
        self.assertEqual(len(self.dll), 0)

        self.dll.add_to_tail(68)
        self.assertEqual(len(self.dll), 1)
        self.assertEqual(self.dll.remove_from_tail(), 68)
        self.assertEqual(len(self.dll), 0)

    def test_list_remove_from_head(self):
        self.dll.remove_from_head()
        self.assertIsNone(self.dll.head)
        self.assertIsNone(self.dll.tail)
        self.assertEqual(len(self.dll), 0)

        self.dll.add_to_head(2)
        self.assertEqual(self.dll.head.value, 2)
        self.assertEqual(self.dll.tail.value, 2)
        self.assertEqual(len(self.dll), 1)
        self.assertEqual(self.dll.remove_from_head(), 2)
        self.assertEqual(len(self.dll), 0)

        self.dll.add_to_head(55)
        self.assertEqual(len(self.dll), 1)
        self.assertEqual(self.dll.remove_from_head(), 55)
        self.assertEqual(len(self.dll), 0)

    def test_list_add_to_tail(self):
        self.assertEqual(self.dll.tail.value, 1)
        self.assertEqual(len(self.dll), 1)

        self.dll.add_to_tail(30)
        self.assertEqual(self.dll.tail.prev.value, 1)
        self.assertEqual(self.dll.tail.value, 30)
        self.assertEqual(len(self.dll), 2)

        self.dll.add_to_tail(20)
        self.assertEqual(self.dll.tail.prev.value, 30)
        self.assertEqual(self.dll.tail.value, 20)
        self.assertEqual(len(self.dll), 3)

    def test_node_delete(self):
        node_1 = ListNode(3)
        node_2 = ListNode(4)
        node_3 = ListNode(5)

        node_1.next = node_2
        node_2.next = node_3
        node_2.prev = node_1
        node_3.prev = node_2

        node_2.delete()

        self.assertEqual(node_1.next, node_3)
        self.assertEqual(node_3.prev, node_1)

    def test_node_insert_before(self):
        self.node.insert_before(0)
        self.assertEqual(self.node.prev.value, 0)

    def test_list_add_to_head(self):
        self.assertEqual(self.dll.head.value, 1)

        self.dll.add_to_head(10)
        self.assertEqual(self.dll.head.value, 10)
        self.assertEqual(self.dll.head.next.value, 1)
        self.assertEqual(len(self.dll), 2)

    def test_node_insert_after(self):
        self.node.insert_after(2)
        self.assertEqual(self.node.next.value, 2)

    def test_list_move_to_end(self):
        self.dll.add_to_head(40)
        self.assertEqual(self.dll.tail.value, 1)
        self.assertEqual(self.dll.head.value, 40)

        self.dll.move_to_end(self.dll.head)
        self.assertEqual(self.dll.tail.value, 40)
        self.assertEqual(self.dll.tail.prev.value, 1)
        self.assertEqual(len(self.dll), 2)

        self.dll.add_to_tail(4)
        self.dll.move_to_end(self.dll.head.next)
        self.assertEqual(self.dll.tail.value, 40)
        self.assertEqual(self.dll.tail.prev.value, 4)
        self.assertEqual(len(self.dll), 3)

    def test_list_move_to_front(self):
        self.dll.add_to_tail(3)
        self.assertEqual(self.dll.head.value, 1)
        self.assertEqual(self.dll.tail.value, 3)

        self.dll.move_to_front(self.dll.tail)
        self.assertEqual(self.dll.head.value, 3)
        self.assertEqual(self.dll.head.next.value, 1)
        self.assertEqual(len(self.dll), 2)

        self.dll.add_to_head(29)
        self.dll.move_to_front(self.dll.head.next)
        self.assertEqual(self.dll.head.value, 3)
        self.assertEqual(self.dll.head.next.value, 29)
        self.assertEqual(len(self.dll), 3)

    def test_list_delete(self):
        self.dll.delete(self.node)
        self.assertIsNone(self.dll.head)
        self.assertIsNone(self.dll.tail)
        self.assertEqual(len(self.dll), 0)

        self.dll.add_to_tail(1)
        self.dll.add_to_head(9)
        self.dll.add_to_tail(6)

        self.dll.delete(self.dll.head)
        self.assertEqual(self.dll.head.value, 1)
        self.assertEqual(self.dll.tail.value, 6)
        self.assertEqual(len(self.dll), 2)

        self.dll.delete(self.dll.head)
        self.assertEqual(self.dll.head.value, 6)
        self.assertEqual(self.dll.tail.value, 6)
        self.assertEqual(len(self.dll), 1)

    def test_get_max(self):
        self.assertEqual(self.dll.get_max(), 1)
        self.dll.add_to_tail(100)
        self.assertEqual(self.dll.get_max(), 100)
        self.dll.add_to_tail(55)
        self.assertEqual(self.dll.get_max(), 100)
        self.dll.add_to_tail(101)
        self.assertEqual(self.dll.get_max(), 101)
Example #25
0
class DoublyLinkedListTests(unittest.TestCase):
  def setUp(self):
    self.node = ListNode(1)
    self.dll = DoublyLinkedList(self.node)

  def test_list_remove_from_tail(self):
    self.dll.add_to_tail(33)
    self.assertEqual(self.dll.remove_from_tail(), 33)

    self.dll.add_to_tail(68)
    self.assertEqual(self.dll.remove_from_tail(), 68)

  def test_list_remove_from_head(self):
    self.dll.add_to_head(2)
    self.assertEqual(self.dll.remove_from_head(), 2)
    
    self.dll.add_to_head(55)
    self.assertEqual(self.dll.remove_from_head(), 55)

  def test_list_add_to_tail(self):
    self.dll.add_to_tail(30)
    self.assertEqual(self.dll.tail.value, 30)

    self.dll.add_to_tail(20)
    self.assertEqual(self.dll.tail.value, 20)

  def test_node_delete(self):
    node_1 = ListNode(3)
    node_2 = ListNode(4)
    node_3 = ListNode(5)

    node_1.next = node_2
    node_2.next = node_3
    node_2.prev = node_1
    node_3.prev = node_2

    node_2.delete()

    self.assertEqual(node_1.next, node_3)
    self.assertEqual(node_3.prev, node_1)

  def test_node_insert_before(self):
    self.node.insert_before(0)
    self.assertEqual(self.node.prev.value, 0)

  def test_list_add_to_head(self):
    self.assertEqual(self.dll.head.value, 1)
    self.dll.add_to_head(10)
    self.assertEqual(self.dll.head.value, 10)
    self.assertEqual(self.dll.head.next.value, 1)

  def test_node_insert_after(self):
    self.node.insert_after(2)
    self.assertEqual(self.node.next.value, 2)

  def test_list_move_to_end(self):
    self.dll.add_to_head(40)
    self.assertEqual(self.dll.tail.value, 1)
    self.assertEqual(self.dll.head.value, 40)

    self.dll.move_to_end(self.dll.head)
    self.assertEqual(self.dll.tail.value, 40)
    self.assertEqual(self.dll.tail.prev.value, 1)

  def test_list_move_to_front(self):
    self.dll.add_to_tail(3)
    self.assertEqual(self.dll.head.value, 1)
    self.assertEqual(self.dll.tail.value, 3)

    self.dll.move_to_front(self.dll.tail)
    self.assertEqual(self.dll.head.value, 3)
    self.assertEqual(self.dll.head.next.value, 1)

  def test_get_max(self):
    # changed the test to be an assertEqual instead of assertIsNone
    # the list is built with a initial '1' so this test should not
    # through none, it should show the single element
    self.assertEqual(self.dll.get_max(), 1)
    self.dll.add_to_tail(100)
    self.assertEqual(self.dll.get_max(), 100)
    self.dll.add_to_tail(55)
    self.assertEqual(self.dll.get_max(), 100)
    self.dll.add_to_tail(101)
    self.assertEqual(self.dll.get_max(), 101)
Example #26
0
    def test_node_delete(self):
        print(" 4---in test_node_delete---")
        node_1 = ListNode(3)
        node_2 = ListNode(4)
        node_3 = ListNode(5)

        node_1.next = node_2
        node_2.next = node_3
        node_2.prev = node_1
        node_3.prev = node_2

        node_2.delete()

        self.assertEqual(node_1.next, node_3)
        self.assertEqual(node_3.prev, node_1)
        print("----out of test_node_delete----")
        print("-------------------------------")
Example #27
0
 def setUp(self):
     self.node = ListNode(1)
     self.dll = DoublyLinkedList(self.node)
Example #28
0
    def test_node_delete(self):
        node_1 = ListNode(3)
        node_2 = ListNode(4)
        node_3 = ListNode(5)

        node_1.next = node_2
        node_2.next = node_3
        node_2.prev = node_1
        node_3.prev = node_2
        # VERY INTERESTING SYNTAX HERE vvv
        node_2.delete()

        self.assertEqual(node_1.next, node_3)
        self.assertEqual(node_3.prev, node_1)