#!/usr/bin/env python3
"""
Generate golden test vectors for LPM Router testbench.
Implements Longest Prefix Match logic against the specified routing table.
"""

import random

def ip_str_to_int(ip_str):
    parts = [int(p) for p in ip_str.split('.')]
    return (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3]

def get_routes():
    """Return list of (prefix_int, mask_len, next_hop) tuples."""
    # Table from spec
    raw_routes = [
        ("10.0.0.0", 8, 1),
        ("10.1.0.0", 16, 2),
        ("10.1.1.0", 24, 3),
        ("192.168.0.0", 16, 4),
        ("192.168.1.0", 24, 5),
        ("172.16.0.0", 12, 6),
        ("8.8.8.0", 24, 7),
        ("0.0.0.0", 0, 0) # Default
    ]
    
    parsed_routes = []
    for p_str, length, hop in raw_routes:
        parsed_routes.append((ip_str_to_int(p_str), length, hop))
    return parsed_routes

def lpm_lookup(ip_int, routes):
    """Find longest matching prefix for given IP."""
    best_len = -1
    best_hop = 0 # Default if nothing matches (though 0.0.0.0/0 covers all)
    
    for prefix, length, hop in routes:
        # Create mask
        if length == 0:
            mask = 0
        else:
            mask = ((1 << 32) - 1) ^ ((1 << (32 - length)) - 1)
        
        # Check match
        if (ip_int & mask) == (prefix & mask):
            if length > best_len:
                best_len = length
                best_hop = hop
                
    return best_hop

def generate_test_cases():
    routes = get_routes()
    test_cases = []
    
    # 1. Specific examples from original spec (good coverage)
    examples = [
        ("10.1.1.5", 3),    # matches /24 (3), /16 (2), /8 (1) -> 3
        ("10.1.2.1", 2),    # matches /16 (2), /8 (1) -> 2
        ("10.2.0.1", 1),    # matches /8 (1) -> 1
        ("192.168.1.100", 5), # matches /24 (5), /16 (4) -> 5
        ("172.20.0.1", 6),  # matches /12 (6) -> 6 (172.16.0.0/12 ranges 172.16-172.31)
        ("8.8.8.8", 7),     # matches /24 (7) -> 7
        ("1.2.3.4", 0)      # matches default -> 0
    ]
    
    for ip_str, expected in examples:
        test_cases.append((ip_str_to_int(ip_str), expected, f"Example {ip_str}"))

    # 2. Edge cases around boundaries
    # 172.16.0.0/12 ranges from 172.16.0.0 to 172.31.255.255
    # Test just inside and just outside
    test_cases.append((ip_str_to_int("172.15.255.255"), 0, "Boundary 172.16 low - 1"))
    test_cases.append((ip_str_to_int("172.16.0.0"), 6, "Boundary 172.16 low"))
    test_cases.append((ip_str_to_int("172.31.255.255"), 6, "Boundary 172.16 high"))
    test_cases.append((ip_str_to_int("172.32.0.0"), 0, "Boundary 172.16 high + 1"))
    
    # 3. Random IP addresses
    random.seed(42)
    for i in range(20):
        ip_int = random.randint(0, 0xFFFFFFFF)
        expected = lpm_lookup(ip_int, routes)
        test_cases.append((ip_int, expected, f"Random {i}"))
        
    return test_cases

def main():
    test_cases = generate_test_cases()
    
    print("// ==============================================")
    print("// LPM Router Golden Test Vectors")
    print("// Generated by: python3 generate_golden.py")
    print("// ==============================================")
    print()
    print(f"localparam NUM_TESTS = {len(test_cases)};")
    print()
    print("reg [31:0] tb_ip [0:{len(test_cases)-1}];")
    print("reg [2:0]  exp_hop [0:{len(test_cases)-1}];")
    print()
    print("initial begin")
    
    for i, (ip, hop, name) in enumerate(test_cases):
        print(f"    // Test {i}: {name}")
        print(f"    tb_ip[{i}] = 32'h{ip:08x};")
        print(f"    exp_hop[{i}] = 3'd{hop};")
        print()
        
    print("end")

if __name__ == "__main__":
    main()
