create and initialize node class instance node20 node20 nodehello world nodehello world nodenone nodenone nodetrue nodetrue get the string representation of this node node10 repr node10 reprnode10 node10 strnode10 node10 node10 node10 create and initialize linkedlist class instance linkedlist linkedlist linkedlist head is none true this function is intended for iterators to access and iterate through data inside linked list linkedlist linkedlist linkedlist inserttailtail linkedlist inserttailtail1 linkedlist inserttailtail2 for node in linkedlist iter used here node tail tail1 tail2 return length of linked list i e number of nodes linkedlist linkedlist lenlinkedlist 0 linkedlist inserttailtail lenlinkedlist 1 linkedlist insertheadhead lenlinkedlist 2 linkedlist deletetail lenlinkedlist 1 linkedlist deletehead lenlinkedlist 0 string representationvisualization of a linked lists linkedlist linkedlist linkedlist inserttail1 linkedlist inserttail3 linkedlist repr 1 3 reprlinkedlist 1 3 strlinkedlist 1 3 linkedlist inserttail5 flinkedlist 1 3 5 indexing support used to get a node at particular position linkedlist linkedlist for i in range0 10 linkedlist insertnthi i allstrlinkedlisti stri for i in range0 10 true linkedlist10 traceback most recent call last valueerror list index out of range linkedlistlenlinkedlist traceback most recent call last valueerror list index out of range used to change the data of a particular node linkedlist linkedlist for i in range0 10 linkedlist insertnthi i linkedlist0 666 linkedlist0 666 linkedlist5 666 linkedlist5 666 linkedlist10 666 traceback most recent call last valueerror list index out of range linkedlistlenlinkedlist 666 traceback most recent call last valueerror list index out of range insert data to the end of linked list linkedlist linkedlist linkedlist inserttailtail linkedlist tail linkedlist inserttailtail2 linkedlist tail tail2 linkedlist inserttailtail3 linkedlist tail tail2 tail3 insert data to the beginning of linked list linkedlist linkedlist linkedlist insertheadhead linkedlist head linkedlist insertheadhead2 linkedlist head2 head linkedlist insertheadhead3 linkedlist head3 head2 head insert data at given index linkedlist linkedlist linkedlist inserttailfirst linkedlist inserttailsecond linkedlist inserttailthird linkedlist first second third linkedlist insertnth1 fourth linkedlist first fourth second third linkedlist insertnth3 fifth linkedlist first fourth second fifth third this method prints every node data linkedlist linkedlist linkedlist inserttailfirst linkedlist inserttailsecond linkedlist inserttailthird linkedlist first second third delete the first node and return the node s data linkedlist linkedlist linkedlist inserttailfirst linkedlist inserttailsecond linkedlist inserttailthird linkedlist first second third linkedlist deletehead first linkedlist second third linkedlist deletehead second linkedlist third linkedlist deletehead third linkedlist deletehead traceback most recent call last indexerror list index out of range delete the tail end node and return the node s data linkedlist linkedlist linkedlist inserttailfirst linkedlist inserttailsecond linkedlist inserttailthird linkedlist first second third linkedlist deletetail third linkedlist first second linkedlist deletetail second linkedlist first linkedlist deletetail first linkedlist deletetail traceback most recent call last indexerror list index out of range delete node at given index and return the node s data linkedlist linkedlist linkedlist inserttailfirst linkedlist inserttailsecond linkedlist inserttailthird linkedlist first second third linkedlist deletenth1 delete middle second linkedlist first third linkedlist deletenth5 this raises error traceback most recent call last indexerror list index out of range linkedlist deletenth1 this also raises error traceback most recent call last indexerror list index out of range check if linked list is empty linkedlist linkedlist linkedlist isempty true linkedlist insertheadfirst linkedlist isempty false this reverses the linked list order linkedlist linkedlist linkedlist inserttailfirst linkedlist inserttailsecond linkedlist inserttailthird linkedlist first second third linkedlist reverse linkedlist third second first store the current node s next node make the current node s nextnode point backwards make the previous node be the current node make the current node the nextnode node to progress iteration return prev in order to put the head at the end testsinglylinkedlist this section of the test used varying data types for input testsinglylinkedlist2 check if it s empty or not delete the head delete the tail delete a node in specific location in linked list add a node instance to its head add none to its tail reverse the linked list create and initialize node class instance node 20 node 20 node hello world node hello world node none node none node true node true get the string representation of this node node 10 __repr__ node 10 repr node 10 node 10 str node 10 node 10 node 10 node 10 create and initialize linkedlist class instance linked_list linkedlist linked_list head is none true this function is intended for iterators to access and iterate through data inside linked list linked_list linkedlist linked_list insert_tail tail linked_list insert_tail tail_1 linked_list insert_tail tail_2 for node in linked_list __iter__ used here node tail tail_1 tail_2 return length of linked list i e number of nodes linked_list linkedlist len linked_list 0 linked_list insert_tail tail len linked_list 1 linked_list insert_head head len linked_list 2 _ linked_list delete_tail len linked_list 1 _ linked_list delete_head len linked_list 0 string representation visualization of a linked lists linked_list linkedlist linked_list insert_tail 1 linked_list insert_tail 3 linked_list __repr__ 1 3 repr linked_list 1 3 str linked_list 1 3 linked_list insert_tail 5 f linked_list 1 3 5 indexing support used to get a node at particular position linked_list linkedlist for i in range 0 10 linked_list insert_nth i i all str linked_list i str i for i in range 0 10 true linked_list 10 traceback most recent call last valueerror list index out of range linked_list len linked_list traceback most recent call last valueerror list index out of range used to change the data of a particular node linked_list linkedlist for i in range 0 10 linked_list insert_nth i i linked_list 0 666 linked_list 0 666 linked_list 5 666 linked_list 5 666 linked_list 10 666 traceback most recent call last valueerror list index out of range linked_list len linked_list 666 traceback most recent call last valueerror list index out of range insert data to the end of linked list linked_list linkedlist linked_list insert_tail tail linked_list tail linked_list insert_tail tail_2 linked_list tail tail_2 linked_list insert_tail tail_3 linked_list tail tail_2 tail_3 insert data to the beginning of linked list linked_list linkedlist linked_list insert_head head linked_list head linked_list insert_head head_2 linked_list head_2 head linked_list insert_head head_3 linked_list head_3 head_2 head insert data at given index linked_list linkedlist linked_list insert_tail first linked_list insert_tail second linked_list insert_tail third linked_list first second third linked_list insert_nth 1 fourth linked_list first fourth second third linked_list insert_nth 3 fifth linked_list first fourth second fifth third link new_node to head print every node data this method prints every node data linked_list linkedlist linked_list insert_tail first linked_list insert_tail second linked_list insert_tail third linked_list first second third delete the first node and return the node s data linked_list linkedlist linked_list insert_tail first linked_list insert_tail second linked_list insert_tail third linked_list first second third linked_list delete_head first linked_list second third linked_list delete_head second linked_list third linked_list delete_head third linked_list delete_head traceback most recent call last indexerror list index out of range delete from tail delete the tail end node and return the node s data linked_list linkedlist linked_list insert_tail first linked_list insert_tail second linked_list insert_tail third linked_list first second third linked_list delete_tail third linked_list first second linked_list delete_tail second linked_list first linked_list delete_tail first linked_list delete_tail traceback most recent call last indexerror list index out of range delete node at given index and return the node s data linked_list linkedlist linked_list insert_tail first linked_list insert_tail second linked_list insert_tail third linked_list first second third linked_list delete_nth 1 delete middle second linked_list first third linked_list delete_nth 5 this raises error traceback most recent call last indexerror list index out of range linked_list delete_nth 1 this also raises error traceback most recent call last indexerror list index out of range test if index is valid default first node check if linked list is empty linked_list linkedlist linked_list is_empty true linked_list insert_head first linked_list is_empty false this reverses the linked list order linked_list linkedlist linked_list insert_tail first linked_list insert_tail second linked_list insert_tail third linked_list first second third linked_list reverse linked_list third second first store the current node s next node make the current node s next_node point backwards make the previous node be the current node make the current node the next_node node to progress iteration return prev in order to put the head at the end test_singly_linked_list this should not happen this should happen this should not happen this should happen this section of the test used varying data types for input test_singly_linked_list_2 check if it s empty or not delete the head delete the tail delete a node in specific location in linked list add a node instance to its head add none to its tail reverse the linked list
from __future__ import annotations

from collections.abc import Iterator
from dataclasses import dataclass
from typing import Any


@dataclass
class Node:
    

    data: Any
    next_node: Node | None = None

    def __repr__(self) -> str:
        
        return f"Node({self.data})"


class LinkedList:
    def __init__(self):
        
        self.head = None

    def __iter__(self) -> Iterator[Any]:
        
        node = self.head
        while node:
            yield node.data
            node = node.next_node

    def __len__(self) -> int:
        
        return sum(1 for _ in self)

    def __repr__(self) -> str:
        
        return " -> ".join([str(item) for item in self])

    def __getitem__(self, index: int) -> Any:
        
        if not 0 <= index < len(self):
            raise ValueError("list index out of range.")
        for i, node in enumerate(self):
            if i == index:
                return node
        return None

    
    def __setitem__(self, index: int, data: Any) -> None:
        
        if not 0 <= index < len(self):
            raise ValueError("list index out of range.")
        current = self.head
        for _ in range(index):
            current = current.next_node
        current.data = data

    def insert_tail(self, data: Any) -> None:
        
        self.insert_nth(len(self), data)

    def insert_head(self, data: Any) -> None:
        
        self.insert_nth(0, data)

    def insert_nth(self, index: int, data: Any) -> None:
        
        if not 0 <= index <= len(self):
            raise IndexError("list index out of range")
        new_node = Node(data)
        if self.head is None:
            self.head = new_node
        elif index == 0:
            new_node.next_node = self.head  
            self.head = new_node
        else:
            temp = self.head
            for _ in range(index - 1):
                temp = temp.next_node
            new_node.next_node = temp.next_node
            temp.next_node = new_node

    def print_list(self) -> None:  
        
        print(self)

    def delete_head(self) -> Any:
        
        return self.delete_nth(0)

    def delete_tail(self) -> Any:  
        
        return self.delete_nth(len(self) - 1)

    def delete_nth(self, index: int = 0) -> Any:
        
        if not 0 <= index <= len(self) - 1:  
            raise IndexError("List index out of range.")
        delete_node = self.head  
        if index == 0:
            self.head = self.head.next_node
        else:
            temp = self.head
            for _ in range(index - 1):
                temp = temp.next_node
            delete_node = temp.next_node
            temp.next_node = temp.next_node.next_node
        return delete_node.data

    def is_empty(self) -> bool:
        
        return self.head is None

    def reverse(self) -> None:
        
        prev = None
        current = self.head

        while current:
            
            next_node = current.next_node
            
            current.next_node = prev
            
            prev = current
            
            current = next_node
        
        self.head = prev


def test_singly_linked_list() -> None:
    
    linked_list = LinkedList()
    assert linked_list.is_empty() is True
    assert str(linked_list) == ""

    try:
        linked_list.delete_head()
        raise AssertionError  
    except IndexError:
        assert True  

    try:
        linked_list.delete_tail()
        raise AssertionError  
    except IndexError:
        assert True  

    for i in range(10):
        assert len(linked_list) == i
        linked_list.insert_nth(i, i + 1)
    assert str(linked_list) == " -> ".join(str(i) for i in range(1, 11))

    linked_list.insert_head(0)
    linked_list.insert_tail(11)
    assert str(linked_list) == " -> ".join(str(i) for i in range(12))

    assert linked_list.delete_head() == 0
    assert linked_list.delete_nth(9) == 10
    assert linked_list.delete_tail() == 11
    assert len(linked_list) == 9
    assert str(linked_list) == " -> ".join(str(i) for i in range(1, 10))

    assert all(linked_list[i] == i + 1 for i in range(9)) is True

    for i in range(9):
        linked_list[i] = -i
    assert all(linked_list[i] == -i for i in range(9)) is True

    linked_list.reverse()
    assert str(linked_list) == " -> ".join(str(i) for i in range(-8, 1))


def test_singly_linked_list_2() -> None:
    
    test_input = [
        -9,
        100,
        Node(77345112),
        "dlrow olleH",
        7,
        5555,
        0,
        -192.55555,
        "Hello, world!",
        77.9,
        Node(10),
        None,
        None,
        12.20,
    ]
    linked_list = LinkedList()

    for i in test_input:
        linked_list.insert_tail(i)

    
    assert linked_list.is_empty() is False
    assert (
        str(linked_list)
        == "-9 -> 100 -> Node(77345112) -> dlrow olleH -> 7 -> 5555 -> "
        "0 -> -192.55555 -> Hello, world! -> 77.9 -> Node(10) -> None -> None -> 12.2"
    )

    
    result = linked_list.delete_head()
    assert result == -9
    assert (
        str(linked_list) == "100 -> Node(77345112) -> dlrow olleH -> 7 -> 5555 -> 0 -> "
        "-192.55555 -> Hello, world! -> 77.9 -> Node(10) -> None -> None -> 12.2"
    )

    
    result = linked_list.delete_tail()
    assert result == 12.2
    assert (
        str(linked_list) == "100 -> Node(77345112) -> dlrow olleH -> 7 -> 5555 -> 0 -> "
        "-192.55555 -> Hello, world! -> 77.9 -> Node(10) -> None -> None"
    )

    
    result = linked_list.delete_nth(10)
    assert result is None
    assert (
        str(linked_list) == "100 -> Node(77345112) -> dlrow olleH -> 7 -> 5555 -> 0 -> "
        "-192.55555 -> Hello, world! -> 77.9 -> Node(10) -> None"
    )

    
    linked_list.insert_head(Node("Hello again, world!"))
    assert (
        str(linked_list)
        == "Node(Hello again, world!) -> 100 -> Node(77345112) -> dlrow olleH -> "
        "7 -> 5555 -> 0 -> -192.55555 -> Hello, world! -> 77.9 -> Node(10) -> None"
    )

    
    linked_list.insert_tail(None)
    assert (
        str(linked_list)
        == "Node(Hello again, world!) -> 100 -> Node(77345112) -> dlrow olleH -> 7 -> "
        "5555 -> 0 -> -192.55555 -> Hello, world! -> 77.9 -> Node(10) -> None -> None"
    )

    
    linked_list.reverse()
    assert (
        str(linked_list)
        == "None -> None -> Node(10) -> 77.9 -> Hello, world! -> -192.55555 -> 0 -> "
        "5555 -> 7 -> dlrow olleH -> Node(77345112) -> 100 -> Node(Hello again, world!)"
    )


def main():
    from doctest import testmod

    testmod()

    linked_list = LinkedList()
    linked_list.insert_head(input("Inserting 1st at head ").strip())
    linked_list.insert_head(input("Inserting 2nd at head ").strip())
    print("\nPrint list:")
    linked_list.print_list()
    linked_list.insert_tail(input("\nInserting 1st at tail ").strip())
    linked_list.insert_tail(input("Inserting 2nd at tail ").strip())
    print("\nPrint list:")
    linked_list.print_list()
    print("\nDelete head")
    linked_list.delete_head()
    print("Delete tail")
    linked_list.delete_tail()
    print("\nPrint list:")
    linked_list.print_list()
    print("\nReverse linked list")
    linked_list.reverse()
    print("\nPrint list:")
    linked_list.print_list()
    print("\nString representation of linked list:")
    print(linked_list)
    print("\nReading/changing Node data using indexing:")
    print(f"Element at Position 1: {linked_list[1]}")
    linked_list[1] = input("Enter New Value: ").strip()
    print("New list:")
    print(linked_list)
    print(f"length of linked_list is : {len(linked_list)}")


if __name__ == "__main__":
    main()