class ObserverVariable:
    def __init__(self, n_crystals=10):
        # Stationary components
        self.Omega_0 = 1.0          # Base will strength
        self.I_0 = 0.5              # Base intelligence
        self.theta_life = 0.1       # Life zone threshold
        
        # Probabilistic components
        self.Omega = self.Omega_0
        self.I = self.I_0
        self.H = 1.0                # Initial entropy
        self.theta = np.pi / 2      # Initial alignment (neutral)
        
        # Crystal filter bank
        self.crystals = [CrystalFilter(i) for i in range(n_crystals)]
        self.weights = np.ones(n_crystals) / n_crystals
        
        # π/e checksum baselines (learned)
        self.C_pi_baseline = 0.5
        self.C_e_baseline = 0.5
        
        # Exhaustion
        self.epsilon = 0.01
        self.energy_spent = 0.0
    
    def compute_observer_function(self, system_input):
        """Compute observer function via crystal filter bank."""
        outputs = []
        for i, crystal in enumerate(self.crystals):
            phi = crystal.process(system_input, self.theta, self.I)
            outputs.append(phi * self.weights[i])
        return np.sum(outputs, axis=0)
    
    def compute_pi_checksum(self, F_o, T=10.0):
        """C_π(O) = ∫ F_o(t) · cos(πt) dt"""
        t = np.linspace(0, T, 1000)
        integrand = F_o(t) * np.cos(np.pi * t / T)
        return np.trapz(integrand, t)
    
    def compute_e_checksum(self, F_o, T=10.0):
        """C_e(O) = ∫ F_o(t) · exp(-et) dt"""
        t = np.linspace(0, T, 1000)
        integrand = F_o(t) * np.exp(-np.e * t / T)
        return np.trapz(integrand, t)
    
    def compute_collapse_potential(self):
        """Δ_c(θ, I) = Δ_max · sin²(θ/2) · (1 - I)"""
        Delta_max = 1.0
        return Delta_max * np.sin(self.theta / 2)**2 * (1 - self.I)
    
    def compute_collapse_rate(self):
        """α_measure = α_base · Δ_c · Ω/(Ω + ε)"""
        alpha_base = 1.0
        Delta_c = self.compute_collapse_potential()
        will_util = self.Omega / (self.Omega + self.epsilon)
        
        # Crystal coherence product
        coherence_prod = 1.0
        for i, crystal in enumerate(self.crystals):
            C_pi_i = crystal.get_checksum()
            coherence_prod *= np.exp(-0.1 * abs(C_pi_i - self.C_pi_baseline))
        
        return alpha_base * Delta_c * will_util * coherence_prod
    
    def measure(self, system_input, ground_truth=None):
        """Perform measurement and update observer state."""
        # Compute observer function
        F_o = self.compute_observer_function(system_input)
        
        # Compute checksums
        C_pi = self.compute_pi_checksum(F_o)
        C_e = self.compute_e_checksum(F_o)
        
        # Compute collapse
        alpha = self.compute_collapse_rate()
        measurement_outcome = self.collapse(system_input, alpha)
        
        # Compute success signal
        if ground_truth is not None:
            success = 1.0 if measurement_outcome == ground_truth else 0.0
        else:
            # Use checksum alignment as proxy
            divergence = abs(C_pi - self.C_pi_baseline) + abs(C_e - self.C_e_baseline)
            success = np.exp(-divergence)
        
        # Update observer state
        self.Omega += 0.1 * success - self.epsilon * self.energy_spent
        self.energy_spent += 0.01
        
        # Update crystal weights based on divergence
        for i, crystal in enumerate(self.crystals):
            C_pi_i = crystal.get_checksum()
            divergence_i = abs(C_pi_i - self.C_pi_baseline)
            if divergence_i < 0.1:
                self.weights[i] += 0.01  # Reward
            else:
                self.weights[i] -= 0.01  # Penalty
        
        # Normalize weights
        self.weights = self.weights / np.sum(self.weights)
        
        return {
            'outcome': measurement_outcome,
            'checksums': {'C_pi': C_pi, 'C_e': C_e},
            'collapse_rate': alpha,
            'will_strength': self.Omega,
            'crystal_weights': self.weights
        }
    
    def collapse(self, system_input, alpha):
        """Collapse system based on alpha."""
        if alpha < 0.1:
            # Low alpha: wave pattern preserved (no collapse)
            return system_input  # Return original
        else:
            # High alpha: particle pattern (forced collapse)
            return self.force_collapse(system_input)
    
    def force_collapse(self, state):
        """Force collapse to single outcome."""
        # Simple implementation: return mode/mean
        if hasattr(state, '__len__'):
            return np.mean(state)
        return state
