"""simple docstring"""







import argparse
from collections import OrderedDict
from pathlib import Path

import torch
from huggingface_hub import hf_hub_download
from PIL import Image
from torchvision.transforms import functional as F

from transformers import DetrImageProcessor, TableTransformerConfig, TableTransformerForObjectDetection
from transformers.utils import logging


logging.set_verbosity_info()
_a  :  List[str]=				logging.get_logger(__name__)

# here we list all keys to be renamed (original name on the left, our name on the right)
_a  :  Optional[Any]=				[]
for i in range(6):
	# encoder layers: output projection, 2 feedforward neural networks and 2 layernorms
	rename_keys.append(
	    (f'''transformer.encoder.layers.{i}.self_attn.out_proj.weight''', f'''encoder.layers.{i}.self_attn.out_proj.weight''')
	)
	rename_keys.append(
	    (f'''transformer.encoder.layers.{i}.self_attn.out_proj.bias''', f'''encoder.layers.{i}.self_attn.out_proj.bias''')
	)
	rename_keys.append((f'''transformer.encoder.layers.{i}.linear1.weight''', f'''encoder.layers.{i}.fc1.weight'''))
	rename_keys.append((f'''transformer.encoder.layers.{i}.linear1.bias''', f'''encoder.layers.{i}.fc1.bias'''))
	rename_keys.append((f'''transformer.encoder.layers.{i}.linear2.weight''', f'''encoder.layers.{i}.fc2.weight'''))
	rename_keys.append((f'''transformer.encoder.layers.{i}.linear2.bias''', f'''encoder.layers.{i}.fc2.bias'''))
	rename_keys.append(
	    (f'''transformer.encoder.layers.{i}.norm1.weight''', f'''encoder.layers.{i}.self_attn_layer_norm.weight''')
	)
	rename_keys.append((f'''transformer.encoder.layers.{i}.norm1.bias''', f'''encoder.layers.{i}.self_attn_layer_norm.bias'''))
	rename_keys.append((f'''transformer.encoder.layers.{i}.norm2.weight''', f'''encoder.layers.{i}.final_layer_norm.weight'''))
	rename_keys.append((f'''transformer.encoder.layers.{i}.norm2.bias''', f'''encoder.layers.{i}.final_layer_norm.bias'''))
	# decoder layers: 2 times output projection, 2 feedforward neural networks and 3 layernorms
	rename_keys.append(
	    (f'''transformer.decoder.layers.{i}.self_attn.out_proj.weight''', f'''decoder.layers.{i}.self_attn.out_proj.weight''')
	)
	rename_keys.append(
	    (f'''transformer.decoder.layers.{i}.self_attn.out_proj.bias''', f'''decoder.layers.{i}.self_attn.out_proj.bias''')
	)
	rename_keys.append(
	    (
	        f'''transformer.decoder.layers.{i}.multihead_attn.out_proj.weight''',
	        f'''decoder.layers.{i}.encoder_attn.out_proj.weight''',
	    )
	)
	rename_keys.append(
	    (
	        f'''transformer.decoder.layers.{i}.multihead_attn.out_proj.bias''',
	        f'''decoder.layers.{i}.encoder_attn.out_proj.bias''',
	    )
	)
	rename_keys.append((f'''transformer.decoder.layers.{i}.linear1.weight''', f'''decoder.layers.{i}.fc1.weight'''))
	rename_keys.append((f'''transformer.decoder.layers.{i}.linear1.bias''', f'''decoder.layers.{i}.fc1.bias'''))
	rename_keys.append((f'''transformer.decoder.layers.{i}.linear2.weight''', f'''decoder.layers.{i}.fc2.weight'''))
	rename_keys.append((f'''transformer.decoder.layers.{i}.linear2.bias''', f'''decoder.layers.{i}.fc2.bias'''))
	rename_keys.append(
	    (f'''transformer.decoder.layers.{i}.norm1.weight''', f'''decoder.layers.{i}.self_attn_layer_norm.weight''')
	)
	rename_keys.append((f'''transformer.decoder.layers.{i}.norm1.bias''', f'''decoder.layers.{i}.self_attn_layer_norm.bias'''))
	rename_keys.append(
	    (f'''transformer.decoder.layers.{i}.norm2.weight''', f'''decoder.layers.{i}.encoder_attn_layer_norm.weight''')
	)
	rename_keys.append(
	    (f'''transformer.decoder.layers.{i}.norm2.bias''', f'''decoder.layers.{i}.encoder_attn_layer_norm.bias''')
	)
	rename_keys.append((f'''transformer.decoder.layers.{i}.norm3.weight''', f'''decoder.layers.{i}.final_layer_norm.weight'''))
	rename_keys.append((f'''transformer.decoder.layers.{i}.norm3.bias''', f'''decoder.layers.{i}.final_layer_norm.bias'''))

# convolutional projection + query embeddings + layernorm of encoder + layernorm of decoder + class and bounding box heads
rename_keys.extend(
    [
        ("input_proj.weight", "input_projection.weight"),
        ("input_proj.bias", "input_projection.bias"),
        ("query_embed.weight", "query_position_embeddings.weight"),
        ("transformer.encoder.norm.weight", "encoder.layernorm.weight"),
        ("transformer.encoder.norm.bias", "encoder.layernorm.bias"),
        ("transformer.decoder.norm.weight", "decoder.layernorm.weight"),
        ("transformer.decoder.norm.bias", "decoder.layernorm.bias"),
        ("class_embed.weight", "class_labels_classifier.weight"),
        ("class_embed.bias", "class_labels_classifier.bias"),
        ("bbox_embed.layers.0.weight", "bbox_predictor.layers.0.weight"),
        ("bbox_embed.layers.0.bias", "bbox_predictor.layers.0.bias"),
        ("bbox_embed.layers.1.weight", "bbox_predictor.layers.1.weight"),
        ("bbox_embed.layers.1.bias", "bbox_predictor.layers.1.bias"),
        ("bbox_embed.layers.2.weight", "bbox_predictor.layers.2.weight"),
        ("bbox_embed.layers.2.bias", "bbox_predictor.layers.2.bias"),
    ]
)
def 						__UpperCAmelCase       (					UpperCAmelCase_					:       Optional[int]    ,							UpperCAmelCase_					:       Tuple    ,							UpperCAmelCase_					:       List[str]						)							->    Optional[Any]:

	'''simple docstring'''





	__snake_case    :							str   	=				state_dict.pop(UpperCAmelCase_						)
	__snake_case    :							Dict   	=				val
def 						__UpperCAmelCase       (					UpperCAmelCase_					:       List[Any]						)							->    Union[str, Any]:

	'''simple docstring'''





	__snake_case    :							Tuple   	=				OrderedDict()
	for key, value in state_dict.items():
		if "backbone.0.body" in key:
			__snake_case    :							int   	=				key.replace('backbone.0.body'    ,							'backbone.conv_encoder.model'						)
			__snake_case    :							List[Any]   	=				value
		else:
			__snake_case    :							int   	=				value

	return new_state_dict
def 						__UpperCAmelCase       (					UpperCAmelCase_					:       str						)							->    Dict:

	'''simple docstring'''





	__snake_case    :							Any   	=				''

	# first: transformer encoder
	for i in range(6						):
		# read in weights + bias of input projection layer (in PyTorch's MultiHeadAttention, this is a single matrix + bias)
		__snake_case    :							Union[str, Any]   	=				state_dict.pop(F"{prefix}transformer.encoder.layers.{i}.self_attn.in_proj_weight"						)
		__snake_case    :							str   	=				state_dict.pop(F"{prefix}transformer.encoder.layers.{i}.self_attn.in_proj_bias"						)
		# next, add query, keys and values (in that order) to the state dict
		__snake_case    :							Tuple   	=				in_proj_weight[:2_56, :]
		__snake_case    :							int   	=				in_proj_bias[:2_56]
		__snake_case    :							Union[str, Any]   	=				in_proj_weight[2_56:5_12, :]
		__snake_case    :							List[Any]   	=				in_proj_bias[2_56:5_12]
		__snake_case    :							Optional[int]   	=				in_proj_weight[-2_56:, :]
		__snake_case    :							Dict   	=				in_proj_bias[-2_56:]
	# next: transformer decoder (which is a bit more complex because it also includes cross-attention)
	for i in range(6						):
		# read in weights + bias of input projection layer of self-attention
		__snake_case    :							Dict   	=				state_dict.pop(F"{prefix}transformer.decoder.layers.{i}.self_attn.in_proj_weight"						)
		__snake_case    :							Optional[int]   	=				state_dict.pop(F"{prefix}transformer.decoder.layers.{i}.self_attn.in_proj_bias"						)
		# next, add query, keys and values (in that order) to the state dict
		__snake_case    :							List[str]   	=				in_proj_weight[:2_56, :]
		__snake_case    :							Any   	=				in_proj_bias[:2_56]
		__snake_case    :							Dict   	=				in_proj_weight[2_56:5_12, :]
		__snake_case    :							Optional[int]   	=				in_proj_bias[2_56:5_12]
		__snake_case    :							List[str]   	=				in_proj_weight[-2_56:, :]
		__snake_case    :							str   	=				in_proj_bias[-2_56:]
		# read in weights + bias of input projection layer of cross-attention
		__snake_case    :							int   	=				state_dict.pop(
		    F"{prefix}transformer.decoder.layers.{i}.multihead_attn.in_proj_weight"						)
		__snake_case    :							str   	=				state_dict.pop(F"{prefix}transformer.decoder.layers.{i}.multihead_attn.in_proj_bias"						)
		# next, add query, keys and values (in that order) of cross-attention to the state dict
		__snake_case    :							List[str]   	=				in_proj_weight_cross_attn[:2_56, :]
		__snake_case    :							Union[str, Any]   	=				in_proj_bias_cross_attn[:2_56]
		__snake_case    :							int   	=				in_proj_weight_cross_attn[2_56:5_12, :]
		__snake_case    :							Optional[Any]   	=				in_proj_bias_cross_attn[2_56:5_12]
		__snake_case    :							str   	=				in_proj_weight_cross_attn[-2_56:, :]
		__snake_case    :							Dict   	=				in_proj_bias_cross_attn[-2_56:]
def 						__UpperCAmelCase       (					UpperCAmelCase_					:       List[str]    ,							UpperCAmelCase_					:       List[Any]						)							->    str:

	'''simple docstring'''





	__snake_case	,       __snake_case    :							int   	=				image.size
	__snake_case    :							Optional[Any]   	=				max(UpperCAmelCase_    ,							UpperCAmelCase_						)
	__snake_case    :							Any   	=				8_00 if 'detection' in checkpoint_url else 10_00
	__snake_case    :							Union[str, Any]   	=				target_max_size / current_max_size
	__snake_case    :							Any   	=				image.resize((int(round(scale * width						)						), int(round(scale * height						)						))						)

	return resized_image
def 						__UpperCAmelCase       (					UpperCAmelCase_					:       Tuple						)							->    List[Any]:

	'''simple docstring'''





	__snake_case    :							Optional[int]   	=				F.to_tensor(UpperCAmelCase_						)
	__snake_case    :							Optional[Any]   	=				F.normalize(UpperCAmelCase_    ,							mean=[0.485, 0.456, 0.406]    ,							std=[0.229, 0.224, 0.225]						)
	return image
@torch.no_grad()
def 						__UpperCAmelCase       (					UpperCAmelCase_					:       Optional[Any]    ,							UpperCAmelCase_					:       str    ,							UpperCAmelCase_					:       Any						)							->    Dict:

	'''simple docstring'''





	logger.info('Converting model...'						)

	# load original state dict
	__snake_case    :							Optional[Any]   	=				torch.hub.load_state_dict_from_url(UpperCAmelCase_    ,							map_location='cpu'						)
	# rename keys
	for src, dest in rename_keys:
		rename_key(UpperCAmelCase_    ,							UpperCAmelCase_    ,							UpperCAmelCase_						)
	__snake_case    :							Dict   	=				rename_backbone_keys(UpperCAmelCase_						)
	# query, key and value matrices need special treatment
	read_in_q_k_v(UpperCAmelCase_						)
	# important: we need to prepend a prefix to each of the base model keys as the head models use different attributes for them
	__snake_case    :							List[str]   	=				'model.'
	for key in state_dict.copy().keys():
		if not key.startswith('class_labels_classifier'						) and not key.startswith('bbox_predictor'						):
			__snake_case    :							Optional[Any]   	=				state_dict.pop(UpperCAmelCase_						)
			__snake_case    :							Union[str, Any]   	=				val
    # create HuggingFace model and load state dict
	__snake_case    :							Union[str, Any]   	=				TableTransformerConfig(
	    backbone='resnet18'    ,							mask_loss_coefficient=1    ,							dice_loss_coefficient=1    ,							ce_loss_coefficient=1    ,							bbox_loss_coefficient=5    ,							giou_loss_coefficient=2    ,							eos_coefficient=0.4    ,							class_cost=1    ,							bbox_cost=5    ,							giou_cost=2    ,							)

	if "detection" in checkpoint_url:
		__snake_case    :							str   	=				15
		__snake_case    :							List[str]   	=				2
		__snake_case    :							Tuple   	=				{0: 'table', 1: 'table rotated'}
		__snake_case    :							Optional[int]   	=				idalabel
		__snake_case    :							Optional[Any]   	=				{v: k for k, v in idalabel.items()}
	else:
		__snake_case    :							Union[str, Any]   	=				1_25
		__snake_case    :							int   	=				6
		__snake_case    :							str   	=				{
		    0: 'table',
		    1: 'table column',
		    2: 'table row',
		    3: 'table column header',
		    4: 'table projected row header',
		    5: 'table spanning cell',
		}
		__snake_case    :							str   	=				idalabel
		__snake_case    :							List[str]   	=				{v: k for k, v in idalabel.items()}

	__snake_case    :							int   	=				DetrImageProcessor(
	    format='coco_detection'    ,							max_size=8_00 if 'detection' in checkpoint_url else 10_00						)
	__snake_case    :							Any   	=				TableTransformerForObjectDetection(UpperCAmelCase_						)
	model.load_state_dict(UpperCAmelCase_						)
	model.eval()

	# verify our conversion
	__snake_case    :							List[Any]   	=				'example_pdf.png' if 'detection' in checkpoint_url else 'example_table.png'
	__snake_case    :							Any   	=				hf_hub_download(repo_id='nielsr/example-pdf'    ,							repo_type='dataset'    ,							filename=UpperCAmelCase_						)
	__snake_case    :							Any   	=				Image.open(UpperCAmelCase_						).convert('RGB'						)
	__snake_case    :							Dict   	=				normalize(resize(UpperCAmelCase_    ,							UpperCAmelCase_						)						).unsqueeze(0						)

	__snake_case    :							Optional[Any]   	=				model(UpperCAmelCase_						)

	if "detection" in checkpoint_url:
		__snake_case    :							Any   	=				(1, 15, 3)
		__snake_case    :							Optional[Any]   	=				torch.tensor(
		    [[-6.7_897, -16.9_985, 6.7_937], [-8.0_186, -22.2_192, 6.9_677], [-7.3_117, -21.0_708, 7.4_055]]						)
		__snake_case    :							int   	=				torch.tensor([[0.4_867, 0.1_767, 0.6_732], [0.6_718, 0.4_479, 0.3_830], [0.4_716, 0.1_760, 0.6_364]]						)

	else:
		__snake_case    :							Any   	=				(1, 1_25, 7)
		__snake_case    :							Any   	=				torch.tensor(
		    [[-18.1_430, -8.3_214, 4.8_274], [-18.4_685, -7.1_361, -4.2_667], [-26.3_693, -9.3_429, -4.9_962]]						)
		__snake_case    :							Union[str, Any]   	=				torch.tensor([[0.4_983, 0.5_595, 0.9_440], [0.4_916, 0.6_315, 0.5_954], [0.6_108, 0.8_637, 0.1_135]]						)

	assert outputs.logits.shape == expected_shape
	assert torch.allclose(outputs.logits[0, :3, :3]    ,							UpperCAmelCase_    ,							atol=1E-4						)
	assert torch.allclose(outputs.pred_boxes[0, :3, :3]    ,							UpperCAmelCase_    ,							atol=1E-4						)
	print('Looks ok!'						)

	if pytorch_dump_folder_path is not None:
		# Save model and image processor
		logger.info(F"Saving PyTorch model and image processor to {pytorch_dump_folder_path}..."						)
		Path(UpperCAmelCase_						).mkdir(exist_ok=UpperCAmelCase_						)
		model.save_pretrained(UpperCAmelCase_						)
		image_processor.save_pretrained(UpperCAmelCase_						)

	if push_to_hub:
		# Push model to HF hub
		logger.info('Pushing model to the hub...'						)
		__snake_case    :							str   	=				(
		    'microsoft/table-transformer-detection'
		    if 'detection' in checkpoint_url
		    else 'microsoft/table-transformer-structure-recognition'
		)
		model.push_to_hub(UpperCAmelCase_						)
		image_processor.push_to_hub(UpperCAmelCase_						)


if __name__ == "__main__":
	_a  :  int=				argparse.ArgumentParser()

	parser.add_argument(
	    "--checkpoint_url",
	    default="https://pubtables1m.blob.core.windows.net/model/pubtables1m_detection_detr_r18.pth",
	    type=str,
	    choices=[
	        "https://pubtables1m.blob.core.windows.net/model/pubtables1m_detection_detr_r18.pth",
	        "https://pubtables1m.blob.core.windows.net/model/pubtables1m_structure_detr_r18.pth",
	    ],
	    help="URL of the Table Transformer checkpoint you'd like to convert.",
	)
	parser.add_argument(
	    "--pytorch_dump_folder_path", default=None, type=str, help="Path to the folder to output PyTorch model."
	)
	parser.add_argument(
	    "--push_to_hub", action="store_true", help="Whether or not to push the converted model to the 🤗 hub."
	)
	_a  :  Dict=				parser.parse_args()
	convert_table_transformer_checkpoint(args.checkpoint_url, args.pytorch_dump_folder_path, args.push_to_hub)






"""simple docstring"""







from typing import Any

class 	UpperCamelCase    :



				def __init__(self       :   List[str]			,						_A       :   Any)					->			int:
					__snake_case    :							Any   	=				data
					__snake_case    :							Dict   	=				None






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







class 	UpperCamelCase    :



				def __init__(self       :   Union[str, Any])					->			Union[str, Any]:
					__snake_case    :							Any   	=				None



				def __iter__(self       :   Tuple)					->			Any:
					__snake_case    :							List[str]   	=				self.head
					while node:
						yield node.data
						__snake_case    :							Any   	=				node.next



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



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



				def __getitem__(self       :   List[Any]			,						_A       :   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       :   int			,						_A       :   int			,						_A       :   Any)					->			None:
					if not 0 <= index < len(self):
						raise ValueError('list index out of range.')
					__snake_case    :							Optional[int]   	=				self.head
					for _ in range(_A):
						__snake_case    :							Any   	=				current.next
					__snake_case    :							Dict   	=				data



				def       _lowercase  (self       :   List[Any]			,						_A       :   Any)					->			None:
					self.insert_nth(len(self)			,						_A)



				def       _lowercase  (self       :   List[str]			,						_A       :   Any)					->			None:
					self.insert_nth(0			,						_A)



				def       _lowercase  (self       :   Optional[Any]			,						_A       :   int			,						_A       :   Any)					->			None:
					if not 0 <= index <= len(self):
						raise IndexError('list index out of range')
					__snake_case    :							str   	=				Node(_A)
					if self.head is None:
						__snake_case    :							str   	=				new_node
					elif index == 0:
						__snake_case    :							Union[str, Any]   	=				self.head  # link new_node to head
						__snake_case    :							int   	=				new_node
					else:
						__snake_case    :							Any   	=				self.head
						for _ in range(index - 1):
							__snake_case    :							Any   	=				temp.next
						__snake_case    :							Dict   	=				temp.next
						__snake_case    :							str   	=				new_node



				def       _lowercase  (self       :   Optional[int])					->			None:  # print every node data
					print(self)



				def       _lowercase  (self       :   Optional[Any])					->			Any:
					return self.delete_nth(0)



				def       _lowercase  (self       :   List[str])					->			Any:  # delete from tail
					return self.delete_nth(len(self) - 1)



				def       _lowercase  (self       :   int			,						_A       :   int = 0)					->			Any:
					if not 0 <= index <= len(self) - 1:  # test if index is valid
						raise IndexError('List index out of range.')
					__snake_case    :							int   	=				self.head  # default first node
					if index == 0:
						__snake_case    :							Any   	=				self.head.next
					else:
						__snake_case    :							List[Any]   	=				self.head
						for _ in range(index - 1):
							__snake_case    :							List[str]   	=				temp.next
						__snake_case    :							Union[str, Any]   	=				temp.next
						__snake_case    :							str   	=				temp.next.next
					return delete_node.data



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






				def       _lowercase  (self       :   Tuple)					->			None:
					__snake_case    :							List[Any]   	=				None
					__snake_case    :							Optional[Any]   	=				self.head

					while current:
						# Store the current node's next node.
						__snake_case    :							List[str]   	=				current.next
						# Make the current node's next point backwards
						__snake_case    :							Optional[Any]   	=				prev
						# Make the previous node be the current node
						__snake_case    :							Optional[Any]   	=				current
						# Make the current node the next node (to progress iteration)
						__snake_case    :							Any   	=				next_node
					# Return prev in order to put the head at the end
					__snake_case    :							Optional[Any]   	=				prev







def 						__UpperCAmelCase       (					)							->    None:

	'''simple docstring'''





	__snake_case    :							Union[str, Any]   	=				LinkedList()
	assert linked_list.is_empty() is True
	assert str(UpperCAmelCase_						) == ""

	try:
		linked_list.delete_head()
		raise AssertionError  # This should not happen.
	except IndexError:
		assert True  # This should happen.

	try:
		linked_list.delete_tail()
		raise AssertionError  # This should not happen.
	except IndexError:
		assert True  # This should happen.

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

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

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

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

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

	linked_list.reverse()
	assert str(UpperCAmelCase_						) == "->".join(str(UpperCAmelCase_						) for i in range(-8    ,							1						)						)
def 						__UpperCAmelCase       (					)							->    None:

	'''simple docstring'''





	__snake_case    :							str   	=				[
	    -9,
	    1_00,
	    Node(77_34_51_12						),
	    'dlrow olleH',
	    7,
	    55_55,
	    0,
	    -192.55_555,
	    'Hello, world!',
	    77.9,
	    Node(10						),
	    None,
	    None,
	    12.20,
	]
	__snake_case    :							Optional[Any]   	=				LinkedList()

	for i in test_input:
		linked_list.insert_tail(UpperCAmelCase_						)

	# Check if it's empty or not
	assert linked_list.is_empty() is False
	assert (
	    str(UpperCAmelCase_						) == "-9->100->Node(77345112)->dlrow olleH->7->5555->0->"
	    "-192.55555->Hello, world!->77.9->Node(10)->None->None->12.2"
	)

	# Delete the head
	__snake_case    :							int   	=				linked_list.delete_head()
	assert result == -9
	assert (
	    str(UpperCAmelCase_						) == "100->Node(77345112)->dlrow olleH->7->5555->0->-192.55555->"
	    "Hello, world!->77.9->Node(10)->None->None->12.2"
	)

	# Delete the tail
	__snake_case    :							Any   	=				linked_list.delete_tail()
	assert result == 12.2
	assert (
	    str(UpperCAmelCase_						) == "100->Node(77345112)->dlrow olleH->7->5555->0->-192.55555->"
	    "Hello, world!->77.9->Node(10)->None->None"
	)

	# Delete a node in specific location in linked list
	__snake_case    :							Tuple   	=				linked_list.delete_nth(10						)
	assert result is None
	assert (
	    str(UpperCAmelCase_						) == "100->Node(77345112)->dlrow olleH->7->5555->0->-192.55555->"
	    "Hello, world!->77.9->Node(10)->None"
	)

	# Add a Node instance to its head
	linked_list.insert_head(Node('Hello again, world!'						)						)
	assert (
	    str(UpperCAmelCase_						)
	    == "Node(Hello again, world!)->100->Node(77345112)->dlrow olleH->"
	    "7->5555->0->-192.55555->Hello, world!->77.9->Node(10)->None"
	)

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

	# Reverse the linked list
	linked_list.reverse()
	assert (
	    str(UpperCAmelCase_						)
	    == "None->None->Node(10)->77.9->Hello, world!->-192.55555->0->5555->"
	    "7->dlrow olleH->Node(77345112)->100->Node(Hello again, world!)"
	)
def 						__UpperCAmelCase       (					)							->    List[Any]:

	'''simple docstring'''





	from doctest import testmod

	testmod()

	__snake_case    :							int   	=				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(UpperCAmelCase_						)
	print('\nReading/changing Node data using indexing:'						)
	print(F"Element at Position 1: {linked_list[1]}"						)
	__snake_case    :							Optional[Any]   	=				input('Enter New Value: '						).strip()
	print('New list:'						)
	print(UpperCAmelCase_						)
	print(F"length of linked_list is : {len(UpperCAmelCase_						)}"						)


if __name__ == "__main__":
	main()





