


'''simple docstring'''





import gc
import threading
import time

import psutil
import torch




class 				UpperCAmelCase			:


 def __init__(      self							:	Tuple ):
    UpperCAmelCase__	:List[Any]      =      psutil.Process()
    UpperCAmelCase__	:Optional[Any]      =      False


 def        __SCREAMING_SNAKE_CASE					(      self							:	str ):
    UpperCAmelCase__	:Optional[Any]      =      -1

    while True:
       UpperCAmelCase__	:Any      =      max(self.process.memory_info().rss			,       self.cpu_memory_peak )

       # can't sleep or will not catch the peak right (this comment is here on purpose)
       if not self.peak_monitoring:
          break


 def        __SCREAMING_SNAKE_CASE					(      self							:	Union[str, Any] ):
    UpperCAmelCase__	:List[Any]      =      True
    UpperCAmelCase__	:Union[str, Any]      =      threading.Thread(target=self.peak_monitor )
    UpperCAmelCase__	:Optional[Any]      =      True
    self.thread.start()





 def        __SCREAMING_SNAKE_CASE					(      self							:	int ):
    UpperCAmelCase__	:List[str]      =      False
    self.thread.join()
    return self.cpu_memory_peak

__lowerCamelCase												=						PeakCPUMemory()


def        a__     (					):
   # Time
   UpperCAmelCase__	:Union[str, Any]      =      {'''time''': time.time()}

   gc.collect()
   torch.cuda.empty_cache()

   # CPU mem
   UpperCAmelCase__	:List[Any]      =      psutil.Process().memory_info().rss
   cpu_peak_tracker.start()

   # GPU mem
   for i in range(torch.cuda.device_count()						):
      UpperCAmelCase__	:Optional[int]      =      torch.cuda.memory_allocated(UpperCamelCase_						)
   torch.cuda.reset_peak_memory_stats()

   return measures


def        a__     (					UpperCamelCase_						:      Optional[Any]						):
   # Time
   UpperCAmelCase__	:Union[str, Any]      =      {'''time''': time.time() - start_measures['''time''']}

   gc.collect()
   torch.cuda.empty_cache()

   # CPU mem
   UpperCAmelCase__	:List[str]      =      (psutil.Process().memory_info().rss - start_measures['''cpu''']) / 2**20
   UpperCAmelCase__	:Optional[Any]      =      (cpu_peak_tracker.stop() - start_measures['''cpu''']) / 2**20

   # GPU mem
   for i in range(torch.cuda.device_count()						):
      UpperCAmelCase__	:List[Any]      =      (torch.cuda.memory_allocated(UpperCamelCase_						) - start_measures[str(UpperCamelCase_						)]) / 2**20
      UpperCAmelCase__	:Any      =      (torch.cuda.max_memory_allocated(UpperCamelCase_						) - start_measures[str(UpperCamelCase_						)]) / 2**20

   return measures


def        a__     (					UpperCamelCase_						:      List[Any],   UpperCamelCase_						:      List[Any]						):
   print(F'''{description}:'''						)
   print(F'''- Time: {measures["time"]:.2f}s'''						)
   for i in range(torch.cuda.device_count()						):
      print(F'''- GPU {i} allocated: {measures[str(UpperCamelCase_						)]:.2f}MiB'''						)
      UpperCAmelCase__	:List[str]      =      measures[F'''{i}-peak''']
      print(F'''- GPU {i} peak: {peak:.2f}MiB'''						)
   print(F'''- CPU RAM allocated: {measures["cpu"]:.2f}MiB'''						)
   print(F'''- CPU RAM peak: {measures["cpu-peak"]:.2f}MiB'''						)







'''simple docstring'''





from multiprocessing import Lock, Pipe, Process

# lock used to ensure that two processes do not access a pipe at the same time
__lowerCamelCase												=						Lock()


def        a__     (					UpperCamelCase_						:      str,   UpperCamelCase_						:      Any,   UpperCamelCase_						:      Any,   UpperCamelCase_						:      int,   UpperCamelCase_						:      Union[str, Any],   UpperCamelCase_						:      List[str],   UpperCamelCase_						:      Optional[int]						):
   global process_lock

   # we perform n swaps since after n swaps we know we are sorted
   # we *could* stop early if we are sorted already, but it takes as long to
   # find out we are sorted as it does to sort the list with this algorithm
   for i in range(0,   10						):
      if (i + position) % 2 == 0 and r_send is not None:
         # send your value to your right neighbor
         process_lock.acquire()
         r_send[1].send(UpperCamelCase_						)
         process_lock.release()

         # receive your right neighbor's value
         process_lock.acquire()
         UpperCAmelCase__	:Union[str, Any]      =      rr_cv[0].recv()
         process_lock.release()

         # take the lower value since you are on the left
         UpperCAmelCase__	:List[Any]      =      min(UpperCamelCase_,   UpperCamelCase_						)
      elif (i + position) % 2 != 0 and l_send is not None:
         # send your value to your left neighbor
         process_lock.acquire()
         l_send[1].send(UpperCamelCase_						)
         process_lock.release()

         # receive your left neighbor's value
         process_lock.acquire()
         UpperCAmelCase__	:List[str]      =      lr_cv[0].recv()
         process_lock.release()

         # take the higher value since you are on the right
         UpperCAmelCase__	:List[Any]      =      max(UpperCamelCase_,   UpperCamelCase_						)
    # after all swaps are performed, send the values back to main
   result_pipe[1].send(UpperCamelCase_						)


def        a__     (					UpperCamelCase_						:      int						):
   UpperCAmelCase__	:List[str]      =      []
   UpperCAmelCase__	:Any      =      []
   # initialize the list of pipes where the values will be retrieved
   for _ in arr:
      result_pipe.append(Pipe()						)
   # creates the processes
   # the first and last process only have one neighbor so they are made outside
   # of the loop
   UpperCAmelCase__	:str      =      Pipe()
   UpperCAmelCase__	:Dict      =      Pipe()
   process_array_.append(
       Process(
           target=UpperCamelCase_,   args=(0, arr[0], None, temp_rs, None, temp_rr, result_pipe[0]),   )						)
   UpperCAmelCase__	:Optional[Any]      =      temp_rs
   UpperCAmelCase__	:Any      =      temp_rr

   for i in range(1,   len(UpperCamelCase_						) - 1						):
      UpperCAmelCase__	:Optional[int]      =      Pipe()
      UpperCAmelCase__	:List[str]      =      Pipe()
      process_array_.append(
          Process(
              target=UpperCamelCase_,   args=(i, arr[i], temp_ls, temp_rs, temp_lr, temp_rr, result_pipe[i]),   )						)
      UpperCAmelCase__	:Optional[Any]      =      temp_rs
      UpperCAmelCase__	:str      =      temp_rr

   process_array_.append(
       Process(
           target=UpperCamelCase_,   args=(
               len(UpperCamelCase_						) - 1,
               arr[len(UpperCamelCase_						) - 1],
               temp_ls,
               None,
               temp_lr,
               None,
               result_pipe[len(UpperCamelCase_						) - 1],
           ),   )						)

   # start the processes
   for p in process_array_:
      p.start()

   # wait for the processes to end and write their values to the list
   for p in range(0,   len(UpperCamelCase_						)						):
      UpperCAmelCase__	:List[str]      =      result_pipe[p][0].recv()
      process_array_[p].join()
   return arr


def        a__     (					):
   UpperCAmelCase__	:Optional[Any]      =      list(range(10,   0,   -1						)						)
   print('''Initial List'''						)
   print(*UpperCamelCase_						)
   UpperCAmelCase__	:str      =      odd_even_transposition(UpperCamelCase_						)
   print('''Sorted List\n'''						)
   print(*UpperCamelCase_						)


if __name__ == "__main__":
    main()



