import math
import random
from typing import List, Optional

AXIS_COHERENCE = 'Cr0'
AXIS_ENTANGLEMENT = 'Cr1'
AXIS_DECOHERENCE = 'Cr2'

class ChromaticState:
    def __init__(self, alpha: float = 1.0, beta: float = 0.0, gamma: float = 0.0):
        self.vector: List[float] = [float(alpha), float(beta), float(gamma)]
        self.normalize()

    @property
    def c0(self) -> float:
        return self.vector[0]

    @property
    def c1(self) -> float:
        return self.vector[1]

    @property
    def c2(self) -> float:
        return self.vector[2]

    def normalize(self):
        mag = math.sqrt(
            self.vector[0] ** 2 + self.vector[1] ** 2 + self.vector[2] ** 2
        )
        if mag == 0:
            self.vector = [1.0, 0.0, 0.0]
        else:
            self.vector[0] /= mag
            self.vector[1] /= mag
            self.vector[2] /= mag

    def evolve(self, delta: float):
        """
        Standard Unitary Evolution U(t).
        Rotates the state within the valid coherence-entanglement plane (Cr0-Cr1).
        """
        theta = delta * 0.5
        c0, c1, _ = self.vector

        next_c0 = c0 * math.cos(theta) - c1 * math.sin(theta)
        next_c1 = c0 * math.sin(theta) + c1 * math.cos(theta)

        self.vector[0] = next_c0
        self.vector[1] = next_c1
        self.normalize()

    def interact(self, shear_intensity: float = 0.1) -> float:
        """
        Applies 'Chromatic Shear'.
        Rotates mass from Coherence (C0) into Decoherence (C2).
        """
        phi = shear_intensity
        c0, _, c2 = self.vector

        next_c0 = c0 * math.cos(phi) - c2 * math.sin(phi)
        next_c2 = c0 * math.sin(phi) + c2 * math.cos(phi)

        self.vector[0] = next_c0
        self.vector[2] = next_c2
        self.normalize()

        return self.calculate_shear()

    def calculate_shear(self) -> float:
        return abs(self.vector[2])

    def measure(self) -> str:
        r = random.random()
        
        def sq(x): return x * x

        p0 = sq(self.vector[0])
        p1 = sq(self.vector[1])

        if r < p0:
            outcome = AXIS_COHERENCE
            self.vector = [1.0, 0.0, 0.0]
        elif r < p0 + p1:
            outcome = AXIS_ENTANGLEMENT
            self.vector = [0.0, 1.0, 0.0]
        else:
            outcome = AXIS_DECOHERENCE
            self.vector = [0.0, 0.0, 1.0]
            
        return outcome

    def __repr__(self):
        return f"ChromaticState([{self.vector[0]:.2f}, {self.vector[1]:.2f}, {self.vector[2]:.2f}])"
