import os
def process_management():
  # Process management
  print("--- Process Management ---")
  print("Current Process ID:", os.getpid())
  print("Parent Process ID:", os.getppid())
  print("Current Login Name:", os.getlogin()) # Getting login name
  print("--------------------------")
def file_management():
  # File management
  print("--- File Management ---")
  file_name = "test.txt"
  file_content = "This is a test file.\nHello, World!"
  # Writing to a file
  with open(file_name, 'w') as f:
    f.write(file_content)
    print("File created and written successfully.")
  # Reading from a file
  with open(file_name, 'r') as f:
    content = f.read()
    print("File content:\n", content)
  print("-----------------------")
def input_output_system_calls():
  # Input / Output system calls
  print("--- Input / Output System Calls ---")
  print("Enter some text:")
  user_input = input()
  print("You entered:", user_input)
  print("-----------------------------------")
if __name__ == "__main__":
  process_management()
  file_management()
  input_output_system_calls()
import random
class Process:
  def __init__(self, pid, burst_time, priority):
     self.pid = pid
     self.burst_time = burst_time
     self.priority = priority
     self.waiting_time = 0
def SJF(processes):
  # Sort processes based on burst time
  processes.sort(key=lambda x: x.burst_time)
  total_waiting_time = 0
  for i in range(len(processes)):
     processes[i].waiting_time = total_waiting_time
     total_waiting_time += processes[i].burst_time
  return total_waiting_time / len(processes)
def Priority(processes):
  # Sort processes based on priority
  processes.sort(key=lambda x: x.priority)
  total_waiting_time = 0
  for i in range(len(processes)):
     processes[i].waiting_time = total_waiting_time
     total_waiting_time += processes[i].burst_time
  return total_waiting_time / len(processes)
def FCFS(processes):
  total_waiting_time = 0
  for i in range(len(processes)):
     processes[i].waiting_time = total_waiting_time
     total_waiting_time += processes[i].burst_time
  return total_waiting_time / len(processes)
def MultiLevelQueue(processes):
  # Divide processes into two queues based on priority
  high_priority_processes = [p for p in processes if p.priority == 1]
  low_priority_processes = [p for p in processes if p.priority == 0]
  # Calculate waiting time for high priority queue
  total_waiting_time_high = FCFS(high_priority_processes)
  # Calculate waiting time for low priority queue
  total_waiting_time_low = FCFS(low_priority_processes)
  # Return the average waiting time of both queues
  return (total_waiting_time_high + total_waiting_time_low) / len(processes)
def generate_processes(num_processes):
  processes = []
  for i in range(num_processes):
    burst_time = random.randint(1, 10)
    priority = random.randint(0, 1)
    processes.append(Process(i+1, burst_time, priority))
  return processes
if __name__ == "__main__":
  num_processes = 5
  processes = generate_processes(num_processes)
  print("Processes:")
  for process in processes:
    print("Process ID:", process.pid, "| Burst Time:", process.burst_time, "| Priority:", process.priority)
  print("\nSJF Average Waiting Time:", SJF(processes))
  print("Priority Average Waiting Time:", Priority(processes))
  print("FCFS Average Waiting Time:", FCFS(processes))
  print("Multi-level Queue Average Waiting Time:", MultiLevelQueue(processes))
class ContiguousAllocation:
  def __init__(self, size):
    self.size = size
    self.blocks = [0] * size # 0 represents free block, 1 represents allocated block
  def allocate(self, file_name, num_blocks):
    allocated_blocks = []
    for i in range(self.size):
       if sum(self.blocks[i:i+num_blocks]) == 0:
            self.blocks[i:i+num_blocks] = [1] * num_blocks
            allocated_blocks = list(range(i, i+num_blocks))
            break
    if allocated_blocks:
       print(f"Allocated {file_name} at blocks: {allocated_blocks}")
    else:
       print(f"Error: Not enough contiguous space available to allocate {file_name}")
  def deallocate(self, file_name, blocks):
    for block in blocks:
       self.blocks[block] = 0
    print(f"Deallocated {file_name} from blocks: {blocks}")
class LinkedListNode:
  def __init__(self, data):
     self.data = data
     self.next = None
class LinkedListAllocation:
  def __init__(self):
     self.head = None
  def allocate(self, file_name, num_blocks):
     allocated_blocks = []
     current = self.head
     for i in range(num_blocks):
       if current:
          allocated_blocks.append(current.data)
          current = current.next
       else:
          print(f"Error: Not enough space available to allocate {file_name}")
          return
     print(f"Allocated {file_name} at blocks: {allocated_blocks}")
  def deallocate(self, file_name, blocks):
     print(f"Deallocated {file_name} from blocks: {blocks}")
class IndirectAllocation:
  def __init__(self, size):
     self.size = size
     self.blocks = [0] * size # 0 represents free block, 1 represents allocated block
     self.index_blocks = [LinkedListAllocation() for _ in range(size)]
  def allocate(self, file_name, num_blocks):
     allocated_blocks = []
    for i in range(self.size):
       if self.blocks[i] == 0:
         allocated_blocks.append(i)
         self.blocks[i] = 1
         for j in range(num_blocks-1):
            i += 1
            if i < self.size:
               self.blocks[i] = 1
               self.index_blocks[i].allocate(file_name, 1)
            else:
               print(f"Error: Not enough space available to allocate {file_name}")
               self.deallocate(file_name, allocated_blocks)
               return
         print(f"Allocated {file_name} at blocks: {allocated_blocks}")
         return
    print(f"Error: Not enough space available to allocate {file_name}")
  def deallocate(self, file_name, blocks):
    for block in blocks:
       self.blocks[block] = 0
       self.index_blocks[block].deallocate(file_name, [block])
    print(f"Deallocated {file_name} from blocks: {blocks}")
if __name__ == "__main__":
  # Contiguous Allocation
  print("Contiguous Allocation:")
  contiguous_allocation = ContiguousAllocation(20)
  contiguous_allocation.allocate("File1", 5)
  contiguous_allocation.allocate("File2", 7)
  contiguous_allocation.deallocate("File1", [0, 1, 2, 3, 4])
  # Linked List Allocation
  print("\nLinked List Allocation:")
  linked_list_allocation = LinkedListAllocation()
linked_list_allocation.head = LinkedListNode(0)
linked_list_allocation.head.next = LinkedListNode(1)
linked_list_allocation.head.next.next = LinkedListNode(2)
linked_list_allocation.head.next.next.next = LinkedListNode(3)
linked_list_allocation.allocate("File1", 3)
linked_list_allocation.deallocate("File1", [0, 1, 2])
# Indirect Allocation
print("\nIndirect Allocation:")
indirect_allocation = IndirectAllocation(20)
indirect_allocation.allocate("File1", 5)
indirect_allocation.allocate("File2", 7)
indirect_allocation.deallocate("File2", [3, 4, 5, 6, 7, 8, 9])
class ContiguousAllocation:
  def __init__(self, size):
    self.size = size
    self.blocks = [0] * size # 0 represents free block, 1 represents allocated block
  def first_fit(self, file_name, num_blocks):
    allocated_blocks = []
    for i in range(self.size):
     if sum(self.blocks[i:i+num_blocks]) == 0:
          self.blocks[i:i+num_blocks] = [1] * num_blocks
          allocated_blocks = list(range(i, i+num_blocks))
          break
  if allocated_blocks:
     print(f"Allocated {file_name} using First-Fit at blocks: {allocated_blocks}")
  else:
     print(f"Error: Not enough contiguous space available to allocate {file_name}")
def best_fit(self, file_name, num_blocks):
  best_fit_index = -1
  min_free_blocks = float('inf')
  for i in range(self.size):
    if self.blocks[i] == 0:
          j=i
          while j < self.size and self.blocks[j] == 0:
            j += 1
          if j - i >= num_blocks and j - i < min_free_blocks:
            best_fit_index = i
            min_free_blocks = j - i
  if best_fit_index != -1:
     self.blocks[best_fit_index:best_fit_index+num_blocks] = [1] * num_blocks
     allocated_blocks = list(range(best_fit_index, best_fit_index+num_blocks))
     print(f"Allocated {file_name} using Best-Fit at blocks: {allocated_blocks}")
  else:
     print(f"Error: Not enough contiguous space available to allocate {file_name}")
def worst_fit(self, file_name, num_blocks):
  worst_fit_index = -1
  max_free_blocks = -1
  for i in range(self.size):
    if self.blocks[i] == 0:
          j=i
          while j < self.size and self.blocks[j] == 0:
              j += 1
            if j - i >= num_blocks and j - i > max_free_blocks:
              worst_fit_index = i
              max_free_blocks = j - i
    if worst_fit_index != -1:
       self.blocks[worst_fit_index:worst_fit_index+num_blocks] = [1] * num_blocks
       allocated_blocks = list(range(worst_fit_index, worst_fit_index+num_blocks))
       print(f"Allocated {file_name} using Worst-Fit at blocks: {allocated_blocks}")
    else:
       print(f"Error: Not enough contiguous space available to allocate {file_name}")
  def deallocate(self, file_name, blocks):
    for block in blocks:
       self.blocks[block] = 0
    print(f"Deallocated {file_name} from blocks: {blocks}")
if __name__ == "__main__":
  contiguous_allocation = ContiguousAllocation(20)
  # Perform allocations
  contiguous_allocation.first_fit("File1", 5)
  contiguous_allocation.best_fit("File2", 7)
  contiguous_allocation.worst_fit("File3", 4)
  # Deallocate a file
  contiguous_allocation.deallocate("File2", [10, 11, 12, 13, 14, 15, 16])
  print("\nRemaining free space:")
  for i in range(len(contiguous_allocation.blocks)):
    if contiguous_allocation.blocks[i] == 0:
       print(f"Block {i} is free.")
class ContiguousAllocation:
  def __init__(self, size):
    self.size = size
    self.blocks = [0] * size # 0 represents free block, 1 represents allocated block
  def allocate(self, file_name, num_blocks):
    allocated_blocks = []
    for i in range(self.size):
       if sum(self.blocks[i:i+num_blocks]) == 0:
            self.blocks[i:i+num_blocks] = [1] * num_blocks
            allocated_blocks = list(range(i, i+num_blocks))
            break
    if allocated_blocks:
       print(f"Allocated {file_name} at blocks: {allocated_blocks}")
    else:
       print(f"Error: Not enough contiguous space available to allocate {file_name}")
  def deallocate(self, file_name, blocks):
    for block in blocks:
       self.blocks[block] = 0
    print(f"Deallocated {file_name} from blocks: {blocks}")
  def calculate_internal_fragmentation(self, allocated_files):
    total_internal_fragmentation = 0
    for file_name, blocks in allocated_files.items():
       total_internal_fragmentation += self.size - len(blocks)
    return total_internal_fragmentation
  def calculate_external_fragmentation(self):
    unused_blocks = 0
    for i in range(len(self.blocks)):
       if self.blocks[i] == 0:
          unused_blocks += 1
    return unused_blocks
if __name__ == "__main__":
  contiguous_allocation = ContiguousAllocation(20)
  # Perform allocations
  allocated_files = {"File1": [0, 1, 2, 3, 4], "File2": [7, 8, 9, 10, 11], "File3": [15, 16, 17, 18]}
  for file_name, blocks in allocated_files.items():
    contiguous_allocation.allocate(file_name, len(blocks))
  # Deallocate a file
  contiguous_allocation.deallocate("File2", [7, 8, 9, 10, 11])
  print("\nRemaining free space:")
  for i in range(len(contiguous_allocation.blocks)):
    if contiguous_allocation.blocks[i] == 0:
       print(f"Block {i} is free.")
  internal_fragmentation = contiguous_allocation.calculate_internal_fragmentation(allocated_files)
  print("\nTotal Internal Fragmentation:", internal_fragmentation)
  external_fragmentation = contiguous_allocation.calculate_external_fragmentation()
  print("Total External Fragmentation:", external_fragmentation)
class ContiguousAllocation:
  def __init__(self, size):
    self.size = size
    self.blocks = [0] * size # 0 represents free block, 1 represents allocated block
  def allocate(self, file_name, num_blocks):
    allocated_blocks = []
    for i in range(self.size):
     if sum(self.blocks[i:i+num_blocks]) == 0:
          self.blocks[i:i+num_blocks] = [1] * num_blocks
          allocated_blocks = list(range(i, i+num_blocks))
          break
  if allocated_blocks:
     print(f"Allocated {file_name} at blocks: {allocated_blocks}")
  else:
     print(f"Error: Not enough contiguous space available to allocate {file_name}")
def deallocate(self, file_name, blocks):
  for block in blocks:
     self.blocks[block] = 0
  print(f"Deallocated {file_name} from blocks: {blocks}")
def compact(self):
  new_blocks = [0] * self.size
  allocated_blocks = []
  distance_moved = 0
  new_pos = 0
  for i in range(self.size):
     if self.blocks[i] == 1:
          allocated_blocks.append(i)
  for block in allocated_blocks:
     new_blocks[new_pos:new_pos+1] = [1]
     distance_moved += abs(new_pos - block)
     new_pos += 1
  self.blocks = new_blocks
  print("\nMemory layout after compaction:")
  for i in range(len(self.blocks)):
     if self.blocks[i] == 1:
         print(f"Block {i} is allocated.")
       else:
         print(f"Block {i} is free.")
    return distance_moved
if __name__ == "__main__":
  contiguous_allocation = ContiguousAllocation(20)
  # Perform allocations
  contiguous_allocation.allocate("File1", 5)
  contiguous_allocation.allocate("File2", 7)
  contiguous_allocation.allocate("File3", 4)
  # Deallocate a file
  contiguous_allocation.deallocate("File2", [10, 11, 12, 13, 14, 15, 16])
  # Compact memory
  total_movement = contiguous_allocation.compact()
  print("\nTotal movement of data during compaction:", total_movement)
class ResourceAllocationGraph:
  def __init__(self, num_processes, num_resources):
    self.num_processes = num_processes
    self.num_resources = num_resources
   self.adj_matrix = [[0] * (num_processes + num_resources) for _ in range(num_processes +
num_resources)]
  def add_edge(self, process, resource):
    self.adj_matrix[process][resource] = 1
  def print_graph(self):
    print("Resource Allocation Graph:")
    for row in self.adj_matrix:
       print(row)
if __name__ == "__main__":
  num_processes = int(input("Enter the number of processes: "))
  num_resources = int(input("Enter the number of resources: "))
  rag = ResourceAllocationGraph(num_processes, num_resources)
  print("Enter the allocations in the format [Process, Resource]:")
  print("Note: Process and Resource indices start from 0.")
  while True:
    user_input = input("Enter allocation (or 'done' to finish): ")
    if user_input.lower() == 'done':
       break
    else:
       try:
            process, resource = map(int, user_input.split(','))
            rag.add_edge(process, num_processes + resource)
       except ValueError:
            print("Invalid input. Please enter allocation in the format [Process, Resource].")
  rag.print_graph()
class BankersAlgorithm:
  def __init__(self, available, max_claim, allocation):
    self.available = available
    self.max_claim = max_claim
    self.allocation = allocation
    self.num_processes = len(allocation)
    self.num_resources = len(available)
  def is_safe_state(self, request):
     # Step 1: Check if the request is within the available resources
     for i in range(self.num_resources):
       if request[i] > self.available[i]:
          print("Request exceeds available resources.")
          return False
     # Step 2: Check if the request can be satisfied
     new_available = self.available[:]
     new_allocation = [self.allocation[i][:] for i in range(self.num_processes)]
     need = [[self.max_claim[i][j] - self.allocation[i][j] for j in range(self.num_resources)] for i in
range(self.num_processes)]
     for i in range(self.num_resources):
       new_available[i] -= request[i]
       new_allocation[request[-1]][i] += request[i]
     safe_sequence = []
     finish = [False] * self.num_processes
     while True:
       found = False
       for i in range(self.num_processes):
          if not finish[i]:
             if all(need[i][j] <= new_available[j] for j in range(self.num_resources)):
                for j in range(self.num_resources):
                   new_available[j] += new_allocation[i][j]
                finish[i] = True
                safe_sequence.append(i)
                found = True
                break
       if not found:
          break
     if all(finish):
       print("System is in a safe state.")
       print("Safe sequence:", safe_sequence)
       return True
    else:
       print("System is in an unsafe state. Deadlock may occur.")
       return False
if __name__ == "__main__":
  available_resources = [3, 3, 2]
  max_claim = [[7, 5, 3], [3, 2, 2], [9, 0, 2], [2, 2, 2], [4, 3, 3]]
  allocation = [[0, 1, 0], [2, 0, 0], [3, 0, 2], [2, 1, 1], [0, 0, 2]]
  process_id = 3
  request = [1, 0, 2, process_id] # Requesting 1 of resource 1 and 2 of resource 3 for process 3
  banker = BankersAlgorithm(available_resources, max_claim, allocation)
  banker.is_safe_state(request)