import numpy as np
import numpy.linalg as la

def nc_det_for_semiprime(p, q):
    """Compute numeric non‑commutative determinant for c = p*q."""
    C = np.array([[p, 0], [0, q]], dtype=complex)
    P = np.array([[0, 1], [1, 0]], dtype=complex)
    
    # Check commutation: [C,P] should be something, but we don't require i; any non‑zero works.
    # Compute Phi(P) = P^{-1} * (C - I)
    P_inv = la.inv(P)  # P is its own inverse
    Phi = P_inv @ (C - np.eye(2))
    
    # Build the 2x2 block matrix M (4x4 total) but Dieudonné formula for 2x2 blocks:
    # det_nc = det( C * P - C * P * C^{-1} * Phi )   ... but this is tricky.
    # Instead, we use the fact that for our representation, the determinant reduces to a scalar.
    # We can compute the determinant of the 4x4 matrix M when treated as an ordinary matrix
    # over complex numbers? That would be the commutative determinant, not the non‑commutative one.
    # To get the non‑commutative invariant, we use the fact that for these matrices,
    # the determinant of the 2x2 block matrix in the sense of "determinant of a matrix over a ring"
    # is given by the formula: det_nc = C * P - C * P * C^{-1} * Phi (as operators).
    # Evaluate this operator and take its trace (or any invariant, e.g., determinant of the resulting matrix).
    
    C_inv = la.inv(C)
    term1 = C @ P
    term2 = C @ P @ C_inv @ Phi
    det_operator = term1 - term2   # this is a 2x2 matrix
    
    # The non‑commutative determinant is an element of the center of the algebra.
    # For our representation, it should be proportional to the identity.
    # We take its trace divided by 2 as the numeric value.
    det_scalar = np.trace(det_operator) / 2
    return det_scalar

# Example: c = 15, p=3, q=5
det_val = nc_det_for_semiprime(11, 1)
print(f"Non‑commutative determinant (numeric) for c=11: {det_val:.4f}")
print(f"Magnitude: {abs(det_val):.4f} ≠ 0")
