import cirq
import random

def make_quantum_teleportation_circuit(ranX, ranY):
    circuit = cirq.Circuit()
    msg, alice, bob = cirq.LineQubit.range(3)
    ## Bell State 
    circuit.append([cirq.H(alice), cirq.CNOT(alice, bob)])
    
    ## Adding random message in message qubit
    circuit.append([cirq.X(msg)**ranX, cirq.Y(msg)**ranY])
    
    ## Entangling msg and Alice qubits
    circuit.append([cirq.CNOT(msg, alice), cirq.H(msg)])
    
    ## Measurements
    circuit.append([cirq.measure(alice, msg)])
    
    ## We use two classical bit from the bell measurement to recover the state of qubit
    circuit.append([cirq.CNOT(alice, bob), cirq.CZ(msg, bob)])
    
    return msg, circuit

def main():
    ## Creating random variable
    ranX = random.random()
    ranY = random.random()
    
    msg, circuit = make_quantum_teleportation_circuit(ranX, ranY)
    
    ## Simulating the circuit
    sim = cirq.Simulator()
    message = sim.simulate(cirq.Circuit.from_ops([cirq.X(msg)**ranX, cirq.Y(msg)**ranY]))
    
    print("Block Spehere of Alice Qubit")
    
    b0x, b0y, b0z = cirq.bloch_vector_from_state_vector(message.final_state, 0)
    print("X: ", round(b0x, 4),
         "Y: ", round(b0y, 4),
         "Z: ", round(b0z, 4))
    
    print("\n Circuit:")
    print(circuit)
    
    final_results = sim.simulate(circuit)
    
    print("\n Block Spehere of Bob's Qubits:")
    b2x, b2y, b2z = cirq.bloch_vector_from_state_vector(final_results.final_state, 2)
    print("X: ", round(b2x, 4),
         "Y: ", round(b2y, 4),
         "Z: ", round(b2z, 4))
    

if __name__ == '__main__':
    main()

