






'''simple docstring'''




from collections import deque

class    _UpperCAmelCase			:




   """simple docstring"""

   def __init__(		self :	List[Any]      ,      __UpperCAmelCase :	str      ,      __UpperCAmelCase :	int      ,      __UpperCAmelCase :	int		):





         '''simple docstring'''





         _A			     =	process_name  # process name
         _A			     =	arrival_time  # arrival time of the process
         # completion time of finished process or last interrupted time
         _A			     =	arrival_time
         _A			     =	burst_time  # remaining burst time
         _A			     =	0  # total time of the process wait in ready queue
         _A			     =	0  # time from arrival time to completion time





class    _UpperCAmelCase			:




   """simple docstring"""

   def __init__(		self :	str      ,      __UpperCAmelCase :	int      ,      __UpperCAmelCase :	list[int]      ,      __UpperCAmelCase :	deque[Process]      ,      __UpperCAmelCase :	int      ,      ):





         '''simple docstring'''





         _A			     =	number_of_queues
         # time slice of queues that round robin algorithm applied
         _A			     =	time_slices
         # unfinished process is in this ready_queue
         _A			     =	queue
         # current time
         _A			     =	current_time
         # finished process is in this sequence queue
         _A			     =	deque()

   def 						lowerCAmelCase (		self :	Any		):





         '''simple docstring'''





         _A			     =	[]
         for i in range(len(self.finish_queue		)		):
               sequence.append(self.finish_queue[i].process_name		)
         return sequence

   def 						lowerCAmelCase (		self :	Any      ,      __UpperCAmelCase :	list[Process]		):





         '''simple docstring'''





         _A			     =	[]
         for i in range(len(__UpperCAmelCase		)		):
               waiting_times.append(queue[i].waiting_time		)
         return waiting_times

   def 						lowerCAmelCase (		self :	List[str]      ,      __UpperCAmelCase :	list[Process]		):





         '''simple docstring'''





         _A			     =	[]
         for i in range(len(__UpperCAmelCase		)		):
               turnaround_times.append(queue[i].turnaround_time		)
         return turnaround_times

   def 						lowerCAmelCase (		self :	Dict      ,      __UpperCAmelCase :	list[Process]		):





         '''simple docstring'''





         _A			     =	[]
         for i in range(len(__UpperCAmelCase		)		):
               completion_times.append(queue[i].stop_time		)
         return completion_times

   def 						lowerCAmelCase (		self :	Any      ,      __UpperCAmelCase :	deque[Process]		):





         '''simple docstring'''





         return [q.burst_time for q in queue]

   def 						lowerCAmelCase (		self :	Optional[Any]      ,      __UpperCAmelCase :	Process		):





         '''simple docstring'''





         process.waiting_time += self.current_time - process.stop_time
         return process.waiting_time

   def 						lowerCAmelCase (		self :	Tuple      ,      __UpperCAmelCase :	deque[Process]		):





         '''simple docstring'''





         _A			     =	deque()  # sequence deque of finished process
         while len(__UpperCAmelCase		) != 0:
               _A			     =	ready_queue.popleft()  # current process

               # if process's arrival time is later than current time, update current time
               if self.current_time < cp.arrival_time:
                     self.current_time += cp.arrival_time

               # update waiting time of current process
               self.update_waiting_time(__UpperCAmelCase		)
               # update current time
               self.current_time += cp.burst_time
               # finish the process and set the process's burst-time 0
               _A			     =	0
               # set the process's turnaround time because it is finished
               _A			     =	self.current_time - cp.arrival_time
               # set the completion time
               _A			     =	self.current_time
               # add the process to queue that has finished queue
               finished.append(__UpperCAmelCase		)

         self.finish_queue.extend(__UpperCAmelCase		)  # add finished process to finish queue
         # FCFS will finish all remaining processes
         return finished

   def 						lowerCAmelCase (		self :	Tuple      ,      __UpperCAmelCase :	deque[Process]      ,      __UpperCAmelCase :	int		):





         '''simple docstring'''





         _A			     =	deque()  # sequence deque of terminated process
         # just for 1 cycle and unfinished processes will go back to queue
         for _ in range(len(__UpperCAmelCase		)		):
               _A			     =	ready_queue.popleft()  # current process

               # if process's arrival time is later than current time, update current time
               if self.current_time < cp.arrival_time:
                     self.current_time += cp.arrival_time

               # update waiting time of unfinished processes
               self.update_waiting_time(__UpperCAmelCase		)
               # if the burst time of process is bigger than time-slice
               if cp.burst_time > time_slice:
                     # use CPU for only time-slice
                     self.current_time += time_slice
                     # update remaining burst time
                     cp.burst_time -= time_slice
                     # update end point time
                     _A			     =	self.current_time
                     # locate the process behind the queue because it is not finished
                     ready_queue.append(__UpperCAmelCase		)
               else:
                     # use CPU for remaining burst time
                     self.current_time += cp.burst_time
                     # set burst time 0 because the process is finished
                     _A			     =	0
                     # set the finish time
                     _A			     =	self.current_time
                     # update the process' turnaround time because it is finished
                     _A			     =	self.current_time - cp.arrival_time
                     # add the process to queue that has finished queue
                     finished.append(__UpperCAmelCase		)

         self.finish_queue.extend(__UpperCAmelCase		)  # add finished process to finish queue
         # return finished processes queue and remaining processes queue
         return finished, ready_queue

   def 						lowerCAmelCase (		self :	str		):





         '''simple docstring'''





         for i in range(self.number_of_queues - 1		):
               _A     ,	_A			     =	self.round_robin(
                   self.ready_queue      ,      self.time_slices[i]		)
         #  the last queue has first_come_first_served algorithm
         self.first_come_first_served(self.ready_queue		)

         return self.finish_queue


if __name__ == "__main__":
    import doctest

    lowerCamelCase_												=					Process('''P1''', 0, 53)
    lowerCamelCase_												=					Process('''P2''', 0, 17)
    lowerCamelCase_												=					Process('''P3''', 0, 68)
    lowerCamelCase_												=					Process('''P4''', 0, 24)
    lowerCamelCase_												=					3
    lowerCamelCase_												=					[17, 25]
    lowerCamelCase_												=					deque([Pa, Pa, Pa, Pa])

    if len(time_slices) != number_of_queues - 1:
        raise SystemExit(0)

    doctest.testmod(extraglobs={'''queue''': deque([Pa, Pa, Pa, Pa])})

    lowerCamelCase_												=					Process('''P1''', 0, 53)
    lowerCamelCase_												=					Process('''P2''', 0, 17)
    lowerCamelCase_												=					Process('''P3''', 0, 68)
    lowerCamelCase_												=					Process('''P4''', 0, 24)
    lowerCamelCase_												=					3
    lowerCamelCase_												=					[17, 25]
    lowerCamelCase_												=					deque([Pa, Pa, Pa, Pa])
    lowerCamelCase_												=					MLFQ(number_of_queues, time_slices, queue, 0)
    lowerCamelCase_												=					mlfq.multi_level_feedback_queue()

    # print total waiting times of processes(P1, P2, P3, P4)
    print(
        F"""waiting time:\
        \t\t\t{MLFQ.calculate_waiting_time(mlfq, [Pa, Pa, Pa, Pa])}"""
    )
    # print completion times of processes(P1, P2, P3, P4)
    print(
        F"""completion time:\
        \t\t{MLFQ.calculate_completion_time(mlfq, [Pa, Pa, Pa, Pa])}"""
    )
    # print total turnaround times of processes(P1, P2, P3, P4)
    print(
        F"""turnaround time:\
        \t\t{MLFQ.calculate_turnaround_time(mlfq, [Pa, Pa, Pa, Pa])}"""
    )
    # print sequence of finished processes
    print(
        F"""sequence of finished processes:\
        {mlfq.calculate_sequence_of_finish_queue()}"""
    )










'''simple docstring'''

from typing import Any

class 	__magic_name__						:







							def __init__(							self					:       List[Any]	,_UpperCAmelCase					:       Any		):
									_a     :      List[Any]        =							data
									_a     :      Union[str, Any]        =							None







							def __repr__(							self					:       Any		):
									return F"""Node({self.data})"""

class 	__magic_name__						:







							def __init__(							self					:       int		):
									_a     :      Tuple        =							None







							def __iter__(							self					:       str		):
									_a     :      int        =							self.head
									while node:
											yield node.data
											_a     :      Union[str, Any]        =							node.next







							def __len__(							self					:       Optional[Any]		):
									return sum(1 for _ in self		)







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







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







							def 							__lowercase			(							self					:       Optional[int]	,_UpperCAmelCase					:       Any		):
									self.insert_nth(len(self		)	,_UpperCAmelCase		)







							def 							__lowercase			(							self					:       Union[str, Any]	,_UpperCAmelCase					:       Any		):
									self.insert_nth(0	,_UpperCAmelCase		)







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







							def 							__lowercase			(							self					:       Optional[int]		):  # print every node data
									print(self		)







							def 							__lowercase			(							self					:       str		):
									return self.delete_nth(0		)







							def 							__lowercase			(							self					:       str		):  # delete from tail
									return self.delete_nth(len(self		) - 1		)







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







							def 							__lowercase			(							self					:       List[Any]		):
									return self.head is None







							def 							__lowercase			(							self					:       Tuple		):
									_a     :      List[Any]        =							None
									_a     :      Tuple        =							self.head

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



def      __lowerCamelCase     (      )			->      None:
		_a     :      List[str]        =							LinkedList()
		assert linked_list.is_empty() is True
		assert str(lowerCAmelCase_  ) == ""

		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(lowerCAmelCase_  ) == i
				linked_list.insert_nth(lowerCAmelCase_						,			i + 1  )
		assert str(lowerCAmelCase_  ) == "->".join(str(lowerCAmelCase_  ) for i in range(1						,			11  )  )

		linked_list.insert_head(0  )
		linked_list.insert_tail(11  )
		assert str(lowerCAmelCase_  ) == "->".join(str(lowerCAmelCase_  ) 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(lowerCAmelCase_  ) == 9
		assert str(lowerCAmelCase_  ) == "->".join(str(lowerCAmelCase_  ) 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  ):
				_a     :      Union[str, Any]        =							-i
		assert all(linked_list[i] == -i for i in range(0						,			9  )  ) is True

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



def      __lowerCamelCase     (      )			->      None:
		_a     :      Dict        =							[
		    -9,
		    100,
		    Node(77345112  ),
		    'dlrow olleH',
		    7,
		    5555,
		    0,
		    -192.55_555,
		    'Hello, world!',
		    77.9,
		    Node(10  ),
		    None,
		    None,
		    12.20,
		]
		_a     :      List[Any]        =							LinkedList()

		for i in test_input:
				linked_list.insert_tail(lowerCAmelCase_  )

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

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

		# Delete the tail
		_a     :      Dict        =							linked_list.delete_tail()
		assert result == 12.2
		assert (
		    str(lowerCAmelCase_  ) == "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
		_a     :      Optional[Any]        =							linked_list.delete_nth(10  )
		assert result is None
		assert (
		    str(lowerCAmelCase_  ) == "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(lowerCAmelCase_  )
		    == "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(lowerCAmelCase_  )
		assert (
		    str(lowerCAmelCase_  )
		    == "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(lowerCAmelCase_  )
		    == "None->None->Node(10)->77.9->Hello, world!->-192.55555->0->5555->"
		    "7->dlrow olleH->Node(77345112)->100->Node(Hello again, world!)"
		)



def      __lowerCamelCase     (      )			->      Union[str, Any]:
		from doctest import testmod

		testmod()

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


if __name__ == "__main__":
							main()




