





"""simple docstring"""




from collections import deque


class 			__UpperCamelCase							:






							def __init__(  self ,   lowerCAmelCase__ ,   lowerCAmelCase__ ,   lowerCAmelCase__ )      ->					Any:
								a :						Tuple      =							process_name  # process name
								a :						int      =							arrival_time  # arrival time of the process
								# completion time of finished process or last interrupted time
								a :						int      =							arrival_time
								a :						Any      =							burst_time  # remaining burst time
								a :						Union[str, Any]      =							0  # total time of the process wait in ready queue
								a :						List[str]      =							0  # time from arrival time to completion time


class 			__UpperCamelCase							:






							def __init__(  self ,   lowerCAmelCase__ ,   lowerCAmelCase__ ,   lowerCAmelCase__ ,   lowerCAmelCase__ ,   )      ->					Any:
								# total number of mlfq's queues
								a :						str      =							number_of_queues
								# time slice of queues that round robin algorithm applied
								a :						Tuple      =							time_slices
								# unfinished process is in this ready_queue
								a :						Any      =							queue
								# current time
								a :						Union[str, Any]      =							current_time
								# finished process is in this sequence queue
								a :						deque[Process]      =							deque()






							def        __a  (  self )      ->					int:
								a :						List[Any]      =							[]
								for i in range(len(self.finish_queue ) ):
									sequence.append(self.finish_queue[i].process_name )
								return sequence






							def        __a  (  self ,   lowerCAmelCase__ )      ->					List[Any]:
								a :						List[Any]      =							[]
								for i in range(len(lowerCAmelCase__ ) ):
									waiting_times.append(queue[i].waiting_time )
								return waiting_times






							def        __a  (  self ,   lowerCAmelCase__ )      ->					Optional[int]:
								a :						Optional[int]      =							[]
								for i in range(len(lowerCAmelCase__ ) ):
									turnaround_times.append(queue[i].turnaround_time )
								return turnaround_times






							def        __a  (  self ,   lowerCAmelCase__ )      ->					List[Any]:
								a :						Union[str, Any]      =							[]
								for i in range(len(lowerCAmelCase__ ) ):
									completion_times.append(queue[i].stop_time )
								return completion_times






							def        __a  (  self ,   lowerCAmelCase__ )      ->					int:
								return [q.burst_time for q in queue]






							def        __a  (  self ,   lowerCAmelCase__ )      ->					Dict:
								process.waiting_time += self.current_time - process.stop_time
								return process.waiting_time






							def        __a  (  self ,   lowerCAmelCase__ )      ->					List[Any]:
								a :						deque[Process]      =							deque()  # sequence deque of finished process
								while len(lowerCAmelCase__ ) != 0:
									a :						List[Any]      =							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(lowerCAmelCase__ )
									# update current time
									self.current_time += cp.burst_time
									# finish the process and set the process's burst-time 0
									a :						str      =							0
									# set the process's turnaround time because it is finished
									a :						Optional[Any]      =							self.current_time - cp.arrival_time
									# set the completion time
									a :						List[Any]      =							self.current_time
									# add the process to queue that has finished queue
									finished.append(lowerCAmelCase__ )

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






							def        __a  (  self ,   lowerCAmelCase__ ,   lowerCAmelCase__ )      ->					int:
								a :						deque[Process]      =							deque()  # sequence deque of terminated process
								# just for 1 cycle and unfinished processes will go back to queue
								for _ in range(len(lowerCAmelCase__ ) ):
									a :						Optional[Any]      =							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(lowerCAmelCase__ )
									# 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 :						str      =							self.current_time
										# locate the process behind the queue because it is not finished
										ready_queue.append(lowerCAmelCase__ )
									else:
										# use CPU for remaining burst time
										self.current_time += cp.burst_time
										# set burst time 0 because the process is finished
										a :						Optional[Any]      =							0
										# set the finish time
										a :						int      =							self.current_time
										# update the process' turnaround time because it is finished
										a :						List[Any]      =							self.current_time - cp.arrival_time
										# add the process to queue that has finished queue
										finished.append(lowerCAmelCase__ )

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






							def        __a  (  self )      ->					str:

								#  all queues except last one have round_robin algorithm
								for i in range(self.number_of_queues - 1 ):
									a :						Optional[int]      =							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

		a		:					Optional[Any]	      =     Process('''P1''', 0, 53)
		a		:					str	      =     Process('''P2''', 0, 17)
		a		:					str	      =     Process('''P3''', 0, 68)
		a		:					int	      =     Process('''P4''', 0, 24)
		a		:					Optional[Any]	      =     3
		a		:					Dict	      =     [17, 25]
		a		:					Dict	      =     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])})

		a		:					List[Any]	      =     Process('''P1''', 0, 53)
		a		:					Dict	      =     Process('''P2''', 0, 17)
		a		:					Tuple	      =     Process('''P3''', 0, 68)
		a		:					Optional[int]	      =     Process('''P4''', 0, 24)
		a		:					int	      =     3
		a		:					Union[str, Any]	      =     [17, 25]
		a		:					Optional[int]	      =     deque([Pa, Pa, Pa, Pa])
		a		:					Optional[int]	      =     MLFQ(number_of_queues, time_slices, queue, 0)
		a		:					Optional[int]	      =     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







def 	_snake_case     (							lowercase__				,       lowercase__				,       lowercase__				,       lowercase__				,       lowercase__				,       ):
				_validation(
				    lowercase__				,       lowercase__				,       lowercase__				,       lowercase__				,       lowercase__				,       )
				# Creates data structures and fill initial step
				_lowerCamelCase	:     dict       =       {}
				_lowerCamelCase	:     dict       =       {}
				for state in states_space:
								_lowerCamelCase	:     Optional[Any]       =       observations_space[0]
								_lowerCamelCase	:     int       =       (
								    initial_probabilities[state] * emission_probabilities[state][observation]
								)
								_lowerCamelCase	:     List[Any]       =       None

				# Fills the data structure with the probabilities of
				# different transitions and pointers to previous states
				for o in range(1				,       len(lowercase__						)						):
								_lowerCamelCase	:     List[str]       =       observations_space[o]
								_lowerCamelCase	:     str       =       observations_space[o - 1]
								for state in states_space:
												# Calculates the argmax for probability function
												_lowerCamelCase	:     Dict       =       ''
												_lowerCamelCase	:     int       =       -1
												for k_state in states_space:
																_lowerCamelCase	:     Any       =       (
																    probabilities[(k_state, prior_observation)]
																    * transition_probabilities[k_state][state]
																    * emission_probabilities[state][observation]
																)
																if probability > max_probability:
																				_lowerCamelCase	:     Dict       =       probability
																				_lowerCamelCase	:     int       =       k_state

            # Update probabilities and pointers dicts
												_lowerCamelCase	:     List[str]       =       (
												    probabilities[(arg_max, prior_observation)]
												    * transition_probabilities[arg_max][state]
												    * emission_probabilities[state][observation]
												)

												_lowerCamelCase	:     Union[str, Any]       =       arg_max

    # The final observation
				_lowerCamelCase	:     Tuple       =       observations_space[len(lowercase__						) - 1]

				# argmax for given final observation
				_lowerCamelCase	:     Optional[int]       =       ''
				_lowerCamelCase	:     str       =       -1
				for k_state in states_space:
								_lowerCamelCase	:     Optional[int]       =       probabilities[(k_state, final_observation)]
								if probability > max_probability:
												_lowerCamelCase	:     int       =       probability
												_lowerCamelCase	:     Optional[int]       =       k_state
				_lowerCamelCase	:     Any       =       arg_max

				# Process pointers backwards
				_lowerCamelCase	:     Optional[Any]       =       last_state
				_lowerCamelCase	:     Union[str, Any]       =       []
				for o in range(len(lowercase__						) - 1				,       -1				,       -1						):
								result.append(lowercase__						)
								_lowerCamelCase	:     Dict       =       pointers[previous, observations_space[o]]
				result.reverse()

				return result







def 	_snake_case     (							lowercase__				,       lowercase__				,       lowercase__				,       lowercase__				,       lowercase__				,       ):
				_validate_not_empty(
				    lowercase__				,       lowercase__				,       lowercase__				,       lowercase__				,       lowercase__				,       )
				_validate_lists(lowercase__				,       lowercase__						)
				_validate_dicts(
				    lowercase__				,       lowercase__				,       lowercase__						)







def 	_snake_case     (							lowercase__				,       lowercase__				,       lowercase__				,       lowercase__				,       lowercase__				,       ):
				if not all(
				    [
				        observations_space,
				        states_space,
				        initial_probabilities,
				        transition_probabilities,
				        emission_probabilities,
				    ]						):
								raise ValueError('There\'s an empty parameter'						)







def 	_snake_case     (							lowercase__				,       lowercase__						):
				_validate_list(lowercase__				,       'observations_space'						)
				_validate_list(lowercase__				,       'states_space'						)







def 	_snake_case     (							lowercase__				,       lowercase__						):
				if not isinstance(_object				,       lowercase__						):
								_lowerCamelCase	:     Tuple       =       f'''{var_name} must be a list'''
								raise ValueError(lowercase__						)
				else:
								for x in _object:
												if not isinstance(lowercase__				,       lowercase__						):
																_lowerCamelCase	:     int       =       f'''{var_name} must be a list of strings'''
																raise ValueError(lowercase__						)







def 	_snake_case     (							lowercase__				,       lowercase__				,       lowercase__				,       ):
				_validate_dict(lowercase__				,       'initial_probabilities'				,       lowercase__						)
				_validate_nested_dict(lowercase__				,       'transition_probabilities'						)
				_validate_nested_dict(lowercase__				,       'emission_probabilities'						)







def 	_snake_case     (							lowercase__				,       lowercase__						):
				_validate_dict(_object				,       lowercase__				,       lowercase__						)
				for x in _object.values():
								_validate_dict(lowercase__				,       lowercase__				,       lowercase__				,       lowercase__						)







def 	_snake_case     (							lowercase__				,       lowercase__				,       lowercase__				,       lowercase__ = False						):
				if not isinstance(_object				,       lowercase__						):
								_lowerCamelCase	:     Tuple       =       f'''{var_name} must be a dict'''
								raise ValueError(lowercase__						)
				if not all(isinstance(lowercase__				,       lowercase__						) for x in _object						):
								_lowerCamelCase	:     Optional[int]       =       f'''{var_name} all keys must be strings'''
								raise ValueError(lowercase__						)
				if not all(isinstance(lowercase__				,       lowercase__						) for x in _object.values()						):
								_lowerCamelCase	:     Optional[int]       =       'nested dictionary ' if nested else ''
								_lowerCamelCase	:     Optional[Any]       =       f'''{var_name} {nested_text}all values must be {value_type.__name__}'''
								raise ValueError(lowercase__						)


if __name__ == "__main__":
			from doctest import testmod

			testmod()