import math

def kron(left, right):
    if not left or not right:
        raise ValueError("tensor factors must be nonempty")
    return tuple(a * b for a in left for b in right)

def norm2(vector):
    return sum(abs(value) ** 2 for value in vector)

def factorability_gap(two_qubit_state):
    if len(two_qubit_state) != 4:
        raise ValueError("factorability fixture expects two qubits")
    a00, a01, a10, a11 = two_qubit_state
    return abs(a00 * a11 - a01 * a10)

def resources(qubits, bytes_per_amplitude=16):
    if qubits < 1 or bytes_per_amplitude < 1:
        raise ValueError("resource parameters must be positive")
    coordinates = 1 << qubits
    return coordinates, coordinates * bytes_per_amplitude

scale = math.sqrt(0.5)
zero, one, plus, plus_i = (1, 0), (0, 1), (scale, scale), (scale, 1j * scale)
product_pairs = ((zero, one), (plus, plus_i), ((0.6, 0.8j), (scale, -scale)))
product_gaps = []
for left, right in product_pairs:
    state = kron(left, right)
    assert abs(norm2(state) - norm2(left) * norm2(right)) < 1e-12
    product_gaps.append(factorability_gap(state))
assert max(product_gaps) < 1e-12

bell_states = ((scale, 0, 0, scale), (0, scale, scale, 0))
entangled_gaps = [factorability_gap(state) for state in bell_states]
assert min(entangled_gaps) > 0.49

ket_010 = kron(kron(zero, one), zero)
assert len(ket_010) == 8 and ket_010[2] == 1
assert kron(one, zero) != kron(zero, one)
for qubits in range(1, 16):
    coordinates, memory = resources(qubits)
    assert coordinates == 2 ** qubits and memory == 16 * coordinates
largest_coordinates, largest_memory = resources(30)
assert largest_coordinates == 1_073_741_824 and largest_memory == 17_179_869_184

empty_rejected = False
try:
    kron((), zero)
except ValueError:
    empty_rejected = True
assert empty_rejected
dimension_rejected = False
try:
    factorability_gap((1, 0, 0))
except ValueError:
    dimension_rejected = True
assert dimension_rejected
print(f"PASS: 13 tensor calculator verifies {len(product_pairs)} product and {len(bell_states)} entangled states; Bell determinant gap={min(entangled_gaps):.3f}, 30-qubit memory={largest_memory / 2**30:.1f} GiB")
