import numpy as np

# Importing standard Qiskit libraries
from qiskit import *
from qiskit.tools.jupyter import *
from qiskit.visualization import *
from ibm_quantum_widgets import *
from qiskit.providers.aer import QasmSimulator
from qiskit.extensions import Initialize
from qiskit_textbook.tools import random_state

# Loading your IBM Quantum account(s)
provider = IBMQ.load_account()

qr =QuantumRegister(3,name='q')
crz=ClassicalRegister(1,name='crz')
crx =ClassicalRegister(1,name='crx')
qc=QuantumCircuit(qr,crz,crx)

def create_bell_pair(qc,a,b):
    qc.h(a)
    qc.cx(a,b)

def alice_gates(qc,psi,a):
    qc.cx(psi,a)
    qc.h(psi)


def measure_and_send(qr,a,b):
    qc.barrier()
    qc.measure(a,0)
    qc.measure(b,1)

def bob_gates(qc,qubit,crz,crx):
    qc.x(qubit).c_if(crx,1)
    qc.z(qubit).c_if(crz,1)

psi =random_state(1)
display(array_to_latex(psi,prefix="|\\psi\\rangle="))
plot_bloch_multivector(psi)

init_gate=Initialize(psi)
init_gate.label='init'
qc.append(init_gate,[0])
qc.barrier()
create_bell_pair(qc,1,2)
qc.barrier()
alice_gates(qc,0,1)
measure_and_send(qc,0,1)
bob_gates(qc,2,crz,crx)
qc.draw()



sim=Aer.get_backend('aer_simulator')
qc.save_statevector()
out_vector=sim.run(qc).result().get_statevector()
plot_bloch_multivector(out_vector)

#usinng qasm sim
inverse_init_gate=init_gate.gates_to_uncompute()



qc.append(init_gate,[0])
qc.barrier()
create_bell_pair(qc,1,2)
qc.barrier()
alice_gates(qc,0,1)
measure_and_send(qc,0,1)
bob_gates(qc,2,crz,crx)
qc.append(inverse_init_gate,[2])
cr_result=ClassicalRegister(1)
qc.add_register(cr_result)
qc.measure(2,2)
qc.draw()


t_qc=transpile(qc,sim)
t_qc.save_statevector()
counts =sim.run(t_qc).result().get_counts()
plot_histogram(counts)





