import sys from typing import NamedTuple # Folding function. def fold(f, z, l): # This is a simple for loop that accumulates a value and then returns it. for x in l: z = f(z, x) return z # This is what the algorithm uses to tell whether a page was used or not. class Bits(NamedTuple): read: bool write: bool # This class manages the state of physical/virtual memory. class State(NamedTuple): faults: int hits: int order: list[int] page_table: dict[int, Bits] physical_capacity: int # Increase hits by 1. def hit(s): return State(s.faults, s.hits + 1, s.order, s.page_table, s.physical_capacity) # Increase faults by 1. def fault(s): return State(s.faults + 1, s.hits, s.order, s.page_table, s.physical_capacity) # Change the mode on a page. def modify(s, page, mode): return State(s.faults, s.hits, s.order, s.page_table | {page: Bits(mode, False)}, s.physical_capacity) # Make space in physical memory depending on the order. def evict(s): def rec(page_table, order, page): # if the page table says that the page's R bit was set: if page_table[page].read == True: # rotate the queue: s_.order[1:] + [page] # change the current page's R bit to False: s_.page_table | {page: Bits(False, False)} # the next page to check for is: s_.order[0] return rec(page_table | {page: Bits(False, False)}, order[1:] + [order[0]], order[0]) else: return page, order last_page, new_order = rec(s.page_table, s.order[1:] + [s.order[0]], s.order[0]) # Finally, return the new state, without that page. return State(s.faults, s.hits, new_order[:-1], {k: v for k, v in s.page_table.items() if k != last_page}, s.physical_capacity) # This function simply inserts a page into the page table. def insert(s, page): return State(s.faults, s.hits, s.order + [page], s.page_table | {page: Bits(False, False)}, s.physical_capacity) # This is the algorithm for second chance page replacement. def second(state, access) -> State: page = access[1] # If the page is in the page table, that is a page hit. if page in state.page_table: # Also, the page's R bit must be set to 1. return state.modify(page, True).hit() else: # If there isn't any space in the page table, call make_space() if len(state.page_table) == state.physical_capacity: return state.evict().insert(page).fault() else: # If there is space in the page table, we can simply insert the page. return state.insert(page).fault() def main(): # The user must provide the physical capacity and the file path to the # access sequence. if len(sys.argv) != 3: print("USAGE:", sys.argv[0], "number_of_pages", "file_path") print("\twhere:\n\t\tnumber_of_pages : int\n\t\tfile_path : string") # The second argument is the file path. file_path = sys.argv[2] # This simply parses the access sequence file. accesses = [] with open(file_path) as f: accesses = [(x.split(":")[0], int(x.split(":")[1])) for x in f.read().split()] # The physical capacity is the first argument. limit = int(sys.argv[1]) # The result is simply the evaluation of the second algorithm on the access # sequence. res = fold(second, State(0, 0, [], dict(), limit), accesses) # Print the result. print("Faults:", res.faults, "\nHits:", res.hits) main()