import numpy as np

class FluxNumber:
    """
    Flux Number: ⟨value, entropy, flux⟩
    Implements addition, multiplication, and collapse operators.
    """
    def __init__(self, value: float, entropy: float = 0.0, flux: float = 0.0):
        self.v = value      # stationary component (mean)
        self.s = entropy    # probability/uncertainty (>=0)
        self.t = flux       # rate of change (dv/dt)

    def __repr__(self):
        return f"Flux(v={self.v:.3f}, σ={self.s:.3f}, τ={self.t:.3f})"

    # ----- Multiplication (⊗) -----
    def __mul__(self, other):
        if not isinstance(other, FluxNumber):
            other = FluxNumber(other)
        new_v = self.v * other.v
        # error propagation: sqrt( (v1·σ2)² + (v2·σ1)² )
        new_s = np.sqrt((self.v * other.s) ** 2 + (other.v * self.s) ** 2)
        # product rule for flux: v1·τ2 + v2·τ1
        new_t = self.v * other.t + other.v * self.t
        return FluxNumber(new_v, new_s, new_t)

    # ----- Collapse (𝒞) : reduce entropy by paying work -----
    def collapse(self, work: float):
        """
        Reduce entropy by 'work' (e.g., computational energy, measurement).
        Entropy cannot go below 0.
        """
        new_s = max(0.0, self.s - work)
        return FluxNumber(self.v, new_s, self.t)

    # ----- Addition (⊕) – included for completeness -----
    def __add__(self, other):
        if not isinstance(other, FluxNumber):
            other = FluxNumber(other)
        return FluxNumber(
            self.v + other.v,
            np.sqrt(self.s**2 + other.s**2),
            self.t + other.t
        )


# ==================== DEMONSTRATION ====================
if __name__ == "__main__":
    print("=== Flux Algebra: Prime Multiplication (c = p₁ × p₂) ===\n")
    a,b = np.random.randint(1000,2000,2)

    # --- Case 1: Exact primes (no uncertainty) ---
    p1_exact = FluxNumber(a, entropy=0.0)
    p2_exact = FluxNumber(b, entropy=0.0)
    c_exact = p1_exact * p2_exact
    print("1) Exact primes (σ = 0):")
    print(f"   p1 = {p1_exact}")
    print(f"   p2 = {p2_exact}")
    print(f"   c = p1 ⊗ p2 = {c_exact}")
    print("   → Entropy remains 0 (deterministic multiplication).\n")

    # --- Case 2: Primes with uncertainty (e.g., from probabilistic test) ---
    # Suppose we know p1 ≈ 61 ± 1.5, p2 ≈ 53 ± 1.0
    p1_noisy = FluxNumber(61, entropy=1.5, flux=0.0)
    p2_noisy = FluxNumber(53, entropy=1.0, flux=0.0)
    c_noisy = p1_noisy * p2_noisy
    print("2) Noisy primes (σ > 0):")
    print(f"   p1 = {p1_noisy}")
    print(f"   p2 = {p2_noisy}")
    print(f"   c = p1 ⊗ p2 = {c_noisy}")
    print("   → Uncertainty propagates: product entropy = √((61·1.0)²+(53·1.5)²) ≈ "
          f"{np.sqrt((61*1.0)**2 + (53*1.5)**2):.3f}\n")

    # --- Case 3: Collapse the product's entropy by paying work ---
    # Work can be interpreted as extra computation (e.g., verifying the product exactly)
    work_needed = c_noisy.s  # pay enough work to reduce entropy to zero
    c_collapsed = c_noisy.collapse(work=work_needed)
    print("3) After collapse with work = {:.3f}:".format(work_needed))
    print(f"   {c_collapsed}")
    print("   → Entropy reduced to 0 (exact product known).\n")

    # --- Optional: dynamic flux (if primes change over time) ---
    p1_dynamic = FluxNumber(a, entropy=0.5, flux=0.2)   # increasing slowly
    p2_dynamic = FluxNumber(b, entropy=0.3, flux=-0.1)  # decreasing
    c_dynamic = p1_dynamic * p2_dynamic
    print("4) Dynamic primes with flux (τ):")
    print(f"   p1 = {p1_dynamic}")
    print(f"   p2 = {p2_dynamic}")
    print(f"   c = {c_dynamic}")
    print("   → Flux component shows how product changes: "
          f"dc/dt ≈ {c_dynamic.t:.3f} per time unit.")
