#Libraries needed to implement and simulate quantum circuits
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit, transpile, qasm2, qasm3
from qiskit_aer import Aer
from qiskit.primitives import BackendSampler
#Custem functions to simplify answers
import Our_Qiskit_Functions as oq #a part of the library presented in arXiv:1903.04359v1.

import numpy as np
import math as m

#Initialize backends simulators to visualize circuits
S_simulator = Aer.backends(name='statevector_simulator')[0]
Q_simulator = Aer.backends(name='qasm_simulator')[0]

q = QuantumRegister(1, name='q')
I_qc = QuantumCircuit(q, name = 'qc')

I_qc.id(q[0])
print('___________Initial____________')
oq.Wavefunction(I_qc)

I_qc.id(q[0])
print('___________Final____________')
oq.Wavefunction(I_qc)

I_qc.draw(output='mpl')

q = QuantumRegister(1, name='q')
H_qc = QuantumCircuit(q, name = 'qc')

H_qc.id(q[0])
print('___________Initial____________')
oq.Wavefunction(I_qc)

H_qc.h(q[0])
print('___________Final____________')
oq.Wavefunction(H_qc)

H_qc.draw(output='mpl')

q = QuantumRegister(1, name='q')
X_qc = QuantumCircuit(q, name = 'qc')

X_qc.id(q[0])
print('___________Initial____________')
oq.Wavefunction(X_qc)

X_qc.x(q[0])
print('___________Final____________')
oq.Wavefunction(X_qc)

X_qc.draw(output='mpl')

q = QuantumRegister(1, name='q')
Y_qc = QuantumCircuit(q, name = 'qc')

Y_qc.id(q[0])
print('___________Initial____________')
oq.Wavefunction(Y_qc)

Y_qc.y(q[0])
print('___________Final____________')
oq.Wavefunction(Y_qc)

Y_qc.draw(output='mpl')

q = QuantumRegister(1, name='q')
Z_qc = QuantumCircuit(q, name = 'qc')

Z_qc.h(q[0])
print('___________Initial____________')
oq.Wavefunction(Z_qc)

Z_qc.z(q[0])
print('___________Final____________')
oq.Wavefunction(Z_qc)

Z_qc.draw(output='mpl')

q = QuantumRegister(1, name='q')
u1_qc = QuantumCircuit(q, name = 'qc')

u1_qc.h(q[0])
print('___________Initial____________')
oq.Wavefunction(u1_qc)

u1_qc.p(m.pi/2,q[0])
print('___________Final____________')
oq.Wavefunction(u1_qc)

u1_qc.draw(output='mpl')

q = QuantumRegister(1, name='q')
S_qc = QuantumCircuit(q, name = 'qc')

S_qc.h(q[0])
print('___________Initial____________')
oq.Wavefunction(S_qc)

S_qc.s(q[0])
print('___________Final____________')
oq.Wavefunction(S_qc)

S_qc.draw(output='mpl')

q = QuantumRegister(1, name='q')
T_qc = QuantumCircuit(q, name = 'qc')

T_qc.h(q[0])
print('___________Initial____________')
oq.Wavefunction(T_qc)

T_qc.t(q[0])
#T_qc.t(q[0])

print('___________Final____________')
oq.Wavefunction(T_qc)

T_qc.draw(output='mpl')

q = QuantumRegister(1, name='q')
Rx_qc = QuantumCircuit(q, name = 'qc')

Rx_qc.id(q[0])
print('___________Initial____________')
oq.Wavefunction(Rx_qc)

Rx_qc.rx(m.pi/2,q[0])
print('___________Final____________')
oq.Wavefunction(Rx_qc)

Rx_qc.draw(output='mpl')

q = QuantumRegister(1, name='q')
Ry_qc = QuantumCircuit(q, name = 'qc')

Ry_qc.id(q[0])
print('___________Initial____________')
oq.Wavefunction(Ry_qc)

Ry_qc.ry(m.pi/2,q[0])
print('___________Final____________')
oq.Wavefunction(Ry_qc)

Ry_qc.draw(output='mpl')

q = QuantumRegister(1, name='q')
Rz_qc = QuantumCircuit(q, name = 'qc')

Rz_qc.h(q[0])
print('___________Initial____________')
oq.Wavefunction(Rz_qc)

Rz_qc.rz(m.pi/2,q[0])

#Rz_qc.t(q[0])
#Rz_qc.x(q[0])
#Rz_qc.t(q[0])
#Rz_qc.x(q[0])

print('___________Final____________')
oq.Wavefunction(Rz_qc)

Rz_qc.draw(output='mpl')

q = QuantumRegister(2, name='q')
Cx_qc = QuantumCircuit(q, name = 'qc')

Cx_qc.h(q[0])
Cx_qc.z(q[0])
Cx_qc.id(q[1])

print('___________Initial____________')
oq.Wavefunction(Cx_qc)

Cx_qc.cx(q[0],q[1])

print('___________Final____________')
oq.Wavefunction(Cx_qc)

Cx_qc.draw(output='mpl')

q = QuantumRegister(2, name='q')
Cz_qc = QuantumCircuit(q, name = 'qc')

Cz_qc.h(q[0])
Cz_qc.x(q[1])

print('___________Initial____________')
oq.Wavefunction(Cz_qc)

Cz_qc.cz(q[0],q[1])

print('___________Final____________')
oq.Wavefunction(Cz_qc)

Cz_qc.draw(output='mpl')

q = QuantumRegister(2, name='q')
cu1_qc = QuantumCircuit(q, name = 'qc')

cu1_qc.h(q[0])
cu1_qc.x(q[1])

print('___________Initial____________')
oq.Wavefunction(cu1_qc)

cu1_qc.cp(m.pi/2,q[0],q[1])

print('___________Final____________')
oq.Wavefunction(cu1_qc)

cu1_qc.draw(output='mpl')

q = QuantumRegister(2, name='q')
swap_qc = QuantumCircuit(q, name = 'qc')

swap_qc.x(q[0])
swap_qc.h(q[1])

print('___________Initial____________')
oq.Wavefunction(swap_qc)

swap_qc.swap(q[0],q[1])

print('___________Final____________')
oq.Wavefunction(swap_qc)

swap_qc.draw(output='mpl')

q = QuantumRegister(3, name='q')
cswap_qc = QuantumCircuit(q, name = 'qc')

cswap_qc.h(q[0])
cswap_qc.x(q[1])
cswap_qc.id(q[2])

print('___________Initial____________')
oq.Wavefunction(cswap_qc)

cswap_qc.cswap(q[0],q[1],q[2])

print('___________Final____________')
oq.Wavefunction(cswap_qc)

cswap_qc.draw(output='mpl')

q = QuantumRegister(3, name='q')
ccnot_qc = QuantumCircuit(q, name = 'qc')

ccnot_qc.x(q[0])
ccnot_qc.x(q[1])
ccnot_qc.h(q[2])
ccnot_qc.z(q[2])

print('___________Initial____________')
oq.Wavefunction(ccnot_qc)

ccnot_qc.ccx(q[0],q[1],q[2])

print('___________Final____________')
oq.Wavefunction(ccnot_qc)

ccnot_qc.draw(output='mpl')