"""
Optimized TNFR primality checker as a console script entry.

Usage after install:
  tnfr-is-prime 17 99991 999983
  tnfr-is-prime --timing 17 997 9973    # With timing
  tnfr-is-prime --cached 17 97 197      # Force cached mode

A number is prime iff ΔNFR(n) == 0, using the TNFR arithmetic pressure equation.
This optimized version uses LRU caching for significant performance improvements.
"""
from __future__ import annotations

import argparse
import time

from functools import lru_cache

# Cached arithmetic functions for optimal performance
@lru_cache(maxsize=10000)
def _divisor_count_cached(n: int) -> int:
    """Optimized divisor count with LRU caching."""
    cnt = 0
    i = 1
    while i * i <= n:
        if n % i == 0:
            cnt += 1
            if i != n // i:
                cnt += 1
        i += 1
    return cnt

@lru_cache(maxsize=10000)
def _divisor_sum_cached(n: int) -> int:
    """Optimized divisor sum with LRU caching."""
    total = 0
    i = 1
    while i * i <= n:
        if n % i == 0:
            total += i
            j = n // i
            if j != i:
                total += j
        i += 1
    return total

@lru_cache(maxsize=10000)
def _prime_factor_count_cached(n: int) -> int:
    """Optimized prime factor count (ω function) with LRU caching."""
    count = 0
    d = 2
    temp_n = n
    while d * d <= temp_n:
        while temp_n % d == 0:
            count += 1
            temp_n //= d
        d += 1
    if temp_n > 1:
        count += 1
    return count

# Non-cached versions for compatibility
def _divisor_count(n: int) -> int:
    """Basic divisor count (τ function)."""
    cnt = 0
    i = 1
    while i * i <= n:
        if n % i == 0:
            cnt += 1
            if i != n // i:
                cnt += 1
        i += 1
    return cnt

def _divisor_sum(n: int) -> int:
    """Basic divisor sum (σ function)."""
    total = 0
    i = 1
    while i * i <= n:
        if n % i == 0:
            total += i
            j = n // i
            if j != i:
                total += j
        i += 1
    return total

def _prime_factor_count(n: int) -> int:
    """Basic prime factor count (ω function)."""
    count = 0
    d = 2
    temp_n = n
    while d * d <= temp_n:
        while temp_n % d == 0:
            count += 1
            temp_n //= d
        d += 1
    if temp_n > 1:
        count += 1
    return count

@lru_cache(maxsize=5000)
def tnfr_delta_nfr_cached(n: int, zeta: float = 1.0, eta: float = 0.8, theta: float = 0.6) -> float:
    """
    Cached TNFR ΔNFR computation for optimal performance.
    
    ΔNFR(n) = ζ·(ω(n)−1) + η·(τ(n)−2) + θ·(σ(n)/n − (1+1/n))
    """
    if n < 2:
        return float('inf')
    
    tau_n = _divisor_count_cached(n)
    sigma_n = _divisor_sum_cached(n)
    omega_n = _prime_factor_count_cached(n)
    
    factorization_pressure = zeta * (omega_n - 1)
    divisor_pressure = eta * (tau_n - 2)
    sigma_pressure = theta * (sigma_n / n - (1 + 1 / n))
    
    return factorization_pressure + divisor_pressure + sigma_pressure

def tnfr_delta_nfr(n: int, *, zeta=1.0, eta=0.8, theta=0.6) -> float:
    """Basic TNFR ΔNFR computation."""
    if n < 2:
        return float('inf')
    tau_n = _divisor_count(n)
    sigma_n = _divisor_sum(n)
    omega_n = _prime_factor_count(n)
    factorization_pressure = zeta * (omega_n - 1)
    divisor_pressure = eta * (tau_n - 2)
    sigma_pressure = theta * (sigma_n / n - (1 + 1 / n))
    return factorization_pressure + divisor_pressure + sigma_pressure

def tnfr_is_prime(n: int, *, use_cached: bool = False) -> tuple[bool, float]:
    """TNFR primality test with optional caching."""
    if use_cached:
        dnfr = tnfr_delta_nfr_cached(n)
    else:
        dnfr = tnfr_delta_nfr(n)
    return (abs(dnfr) == 0.0, dnfr)

def benchmark_basic(max_n: int = 10000, sample_size: int = 100) -> dict:
    """Simple benchmark of TNFR primality testing."""
    import random
    
    # Generate test numbers
    test_numbers = list(range(2, 30))  # Small numbers
    test_numbers.extend([97, 997, 9973, 99991])  # Known primes
    
    # Add random samples
    random.seed(42)
    if max_n > 100:
        test_numbers.extend(random.sample(range(100, min(max_n, 10000)), min(sample_size, max_n - 100)))
    
    # Test basic implementation
    start = time.perf_counter()
    basic_results = []
    for n in test_numbers:
        is_prime, delta_nfr = tnfr_is_prime(n, use_cached=False)
        basic_results.append((n, is_prime, delta_nfr))
    basic_time = time.perf_counter() - start
    
    # Test cached implementation  
    start = time.perf_counter()
    cached_results = []
    for n in test_numbers:
        is_prime, delta_nfr = tnfr_is_prime(n, use_cached=True)
        cached_results.append((n, is_prime, delta_nfr))
    cached_time = time.perf_counter() - start
    
    # Second run to test cache effectiveness
    start = time.perf_counter()
    for n in test_numbers:
        tnfr_is_prime(n, use_cached=True)
    cached_time_2nd = time.perf_counter() - start
    
    prime_count = sum(1 for _, is_prime, _ in basic_results if is_prime)
    
    return {
        'total_numbers': len(test_numbers),
        'primes_found': prime_count,
        'prime_ratio': prime_count / len(test_numbers),
        'basic_time_ms': basic_time * 1000,
        'cached_time_ms': cached_time * 1000,
        'cached_2nd_time_ms': cached_time_2nd * 1000,
        'speedup_1st_run': basic_time / cached_time if cached_time > 0 else 0,
        'speedup_2nd_run': basic_time / cached_time_2nd if cached_time_2nd > 0 else 0,
        'cache_info': {
            'delta_nfr': tnfr_delta_nfr_cached.cache_info()._asdict(),
            'divisor_count': _divisor_count_cached.cache_info()._asdict(),
            'divisor_sum': _divisor_sum_cached.cache_info()._asdict(),
            'prime_factor_count': _prime_factor_count_cached.cache_info()._asdict(),
        }
    }

def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(
        description="Optimized TNFR primality check using ΔNFR equations",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=\"\"\"
Examples:
  tnfr-is-prime 17 99991 999983          # Basic usage
  tnfr-is-prime --cached 982451653       # Use caching
  tnfr-is-prime --benchmark 10000        # Benchmark mode  
  tnfr-is-prime --timing 17 97           # With timing
        \"\"\"
    )
    
    parser.add_argument("numbers", nargs="*", type=int, help="Integers to check")
    parser.add_argument("--cached", action="store_true", help="Use cached implementation")
    parser.add_argument("--timing", action="store_true", help="Show timing information")
    parser.add_argument("--benchmark", type=int, metavar="N", help="Run benchmark up to N")
    parser.add_argument("--stats", action="store_true", help="Show cache statistics")
    
    args = parser.parse_args(argv)
    
    # Benchmark mode
    if args.benchmark:
        print(f"Running TNFR primality benchmark up to {args.benchmark}...")
        results = benchmark_basic(args.benchmark)
        
        print("\\nBenchmark Results:")
        print("=" * 50)
        print(f"Numbers tested: {results['total_numbers']:,}")
        print(f"Primes found: {results['primes_found']:,}")
        print(f"Prime ratio: {results['prime_ratio']:.4f}")
        print(f"Basic implementation: {results['basic_time_ms']:.2f} ms")
        print(f"Cached (1st run): {results['cached_time_ms']:.2f} ms")
        print(f"Cached (2nd run): {results['cached_2nd_time_ms']:.2f} ms")
        print(f"Speedup (1st run): {results['speedup_1st_run']:.2f}x")
        print(f"Speedup (2nd run): {results['speedup_2nd_run']:.2f}x")
        
        if args.stats:
            print("\\nCache Statistics:")
            print("-" * 30)
            for func_name, cache_info in results['cache_info'].items():
                print(f"{func_name}:")
                print(f"  Hits: {cache_info['hits']:,}")
                print(f"  Misses: {cache_info['misses']:,}")
                print(f"  Hit rate: {cache_info['hits']/(cache_info['hits']+cache_info['misses']):.1%}")
        
        return 0
    
    if not args.numbers:
        parser.print_help()
        return 1
    
    # Standard mode
    if args.timing:
        header = f"{'n':>12}  {'TNFR_PRIME':>11}  {'ΔNFR':>14}  {'Time(μs)':>10}"
    else:
        header = f"{'n':>12}  {'TNFR_PRIME':>11}  {'ΔNFR':>14}"
    
    print(header)
    print("-" * len(header))
    
    total_time = 0
    for n in args.numbers:
        if args.timing:
            start = time.perf_counter()
        
        is_prime, delta_nfr = tnfr_is_prime(n, use_cached=args.cached)
        
        if args.timing:
            elapsed_us = (time.perf_counter() - start) * 1_000_000
            total_time += elapsed_us
            print(f"{n:12d}  {str(is_prime):>11}  {delta_nfr:14.6f}  {elapsed_us:10.2f}")
        else:
            print(f"{n:12d}  {str(is_prime):>11}  {delta_nfr:14.6f}")
    
    # Show cache statistics if requested
    if args.stats and args.cached:
        print("\\nCache Statistics:")
        print("-" * 25)
        cache_stats = {
            'delta_nfr': tnfr_delta_nfr_cached.cache_info(),
            'divisor_count': _divisor_count_cached.cache_info(),
            'divisor_sum': _divisor_sum_cached.cache_info(),
            'prime_factor_count': _prime_factor_count_cached.cache_info(),
        }
        
        total_hits = sum(info.hits for info in cache_stats.values())
        total_requests = sum(info.hits + info.misses for info in cache_stats.values())
        overall_hit_rate = total_hits / total_requests if total_requests > 0 else 0
        
        print(f"Overall hit rate: {overall_hit_rate:.1%}")
        for func_name, cache_info in cache_stats.items():
            requests = cache_info.hits + cache_info.misses
            if requests > 0:
                hit_rate = cache_info.hits / requests
                print(f"{func_name}: {cache_info.hits}/{requests} ({hit_rate:.1%})")
    
    if args.timing and len(args.numbers) > 1:
        avg_time = total_time / len(args.numbers)
        print(f"\\nAverage time: {avg_time:.2f} μs/number")
    
    return 0

if __name__ == "__main__":
    raise SystemExit(main())