import sys

class CoherenceSimulator:
    def __init__(self, protocol):
        self.protocol = protocol
        # State storage: {address: [core0_state, core1_state, core2_state, core3_state]}
        # Initial state is 'I' (Invalid)
        self.cache_states = {}

    def get_states(self, addr):
        if addr not in self.cache_states:
            self.cache_states[addr] = ['I'] * 4
        return self.cache_states[addr]

    def process_trace(self, trace_line):
        # Format: <CoreID> <Operation> <Address>
        # Example: 0 R 0xA
        parts = trace_line.split()
        if not parts: return
        core_id = int(parts[0])
        op = parts[1] # 'R' for Read, 'W' for Write
        addr = parts[2]
        
        states = self.get_states(addr)
        old_states = list(states)
        
        if self.protocol == "MSI":
            self.transition_msi(core_id, op, states)
        elif self.protocol == "MESI":
            self.transition_mesi(core_id, op, states)
            
        self.print_output(core_id, op, addr, old_states, states)

    def transition_msi(self, core_id, op, states):
        """ MSI Logic """
        if op == 'R':
            if states[core_id] == 'I':
                # BusRead: If any core is in Modified, it must write back and move to Shared
                for i in range(4):
                    if states[i] == 'M':
                        states[i] = 'S'
                states[core_id] = 'S'
        elif op == 'W':
            if states[core_id] != 'M':
                # BusWrite/Invalidate: All other cores move to Invalid
                for i in range(4):
                    if i != core_id:
                        states[i] = 'I'
                states[core_id] = 'M'

    def transition_mesi(self, core_id, op, states):
        """ MESI Logic """
        if op == 'R':
            if states[core_id] == 'I':
                # Check if any other core has a copy
                others = [states[i] for i in range(4) if i != core_id]
                is_shared = any(s in ['S', 'M', 'E'] for s in others)
                
                if is_shared:
                    # BusRead: Downgrade others and move to Shared
                    for i in range(4):
                        if states[i] == 'M' or states[i] == 'E':
                            states[i] = 'S'
                    states[core_id] = 'S'
                else:
                    # No other core has it: Move to Exclusive
                    states[core_id] = 'E'
        elif op == 'W':
            if states[core_id] != 'M':
                # BusUpgrade/Invalidate: All other cores move to Invalid
                for i in range(4):
                    if i != core_id:
                        states[i] = 'I'
                states[core_id] = 'M'

    def print_output(self, core_id, op, addr, old, new):
        # Specific Output Format: 
        # [Core X: Op Addr] Old_States -> New_States
        old_str = ",".join(old)
        new_str = ",".join(new)
        print(f"[{core_id}: {op} {addr}] {old_str} -> {new_str}")

def run_sim(protocol, trace_file):
    sim = CoherenceSimulator(protocol)
    print(f"--- Running {protocol} Protocol ---")
    try:
        with open(trace_file, 'r') as f:
            for line in f:
                if line.strip():
                    sim.process_trace(line)
    except FileNotFoundError:
        print(f"Error: Trace file '{trace_file}' not found.")

if __name__ == "__main__":
    # Usage: python sim.py <MSI|MESI> <trace_file>
    if len(sys.argv) < 3:
        print("Usage: python sim.py <MSI|MESI> <trace_file>")
    else:
        run_sim(sys.argv[1], sys.argv[2])