#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(3)
c = ClassicalRegister(3)
super0 = QuantumCircuit(q, c)

super0.h(q[0])
super0.id(q[1])
super0.id(q[2])

super0.measure(q[0], c[0])

#print(super0.qasm3())
print(qasm3.dumps(super0))

q = QuantumRegister(1, name='q')
c = ClassicalRegister(1, name='c')
super0 = QuantumCircuit(q, c,name='qc')

super0.h(q[0])

#Inst = super0.qasm()
Inst = qasm2.dumps(super0)

print(Inst[36:len(Inst)])

q = QuantumRegister(2, name='q')
c = ClassicalRegister(2, name='c')
two_q = QuantumCircuit(q, c,name='qc')

two_q.h(q[0])
two_q.h(q[1])
two_q.measure(q[0],c[0])

print('____________QuantumCircuit.qasm()_____________________')
qasm = qasm2.dumps(two_q)
print(qasm[36:len(qasm)])

print('____________QuantumCircuit.data_____________________')
print(two_q.data)

print('____________QuantumCircuit.qregs_____________________')
print(two_q.qregs)

print('____________QuantumCircuit.cregs_____________________')
print(two_q.cregs)

q = QuantumRegister(2, name='q')
c = ClassicalRegister(2, name='c')
qc = QuantumCircuit(q, c,name='qc')

qc.h(q[0])
qc.h(q[1])
qc.measure(q[0],c[0])

print('_________Initial___________')
qasm = qasm2.dumps(qc)
print(qasm[36:len(qasm)])

inst = qc.data[1]

del qc.data[1]
print('______________del qc.data[1]_____________________')
qasm = qasm2.dumps(qc)
print(qasm[36:len(qasm)])

qc.data.append(inst)
print('______________qc.data.append(inst)_____________________')
qasm = qasm2.dumps(qc)
print(qasm[36:len(qasm)])

qc.data.insert(0,inst)
print('______________qc.data.insert(0,inst)_____________________')
qasm = qasm2.dumps(qc)
print(qasm[36:len(qasm)])



q = QuantumRegister(2, name='q')
c = ClassicalRegister(2, name='c')
qc = QuantumCircuit(q, c,name='qc')

q2 = QuantumRegister(2, name='q2')
c2 = ClassicalRegister(2, name='c2')
qc2 = QuantumCircuit(q2, c2,name='qc2')

qc.h(q[0])
qc.measure(q[0],c[0])
qc2.h(q2[0])
      
print('__________qc______________')
qasm = qasm2.dumps(qc)
print(qasm[36:len(qasm)])

print('__________qc2______________')
qasm = qasm2.dumps(qc2)
print(qasm[36:len(qasm)])

q = QuantumRegister(1, name='q')
c = ClassicalRegister(1, name='c')
qc = QuantumCircuit(q, c,name='qc')

q2 = QuantumRegister(1, name='q2')
c2 = ClassicalRegister(1, name='c2')
qc2 = QuantumCircuit(q2, c2,name='qc2')

qc.add_register(c2)
qc2.add_register(q)

qc.h(q[0])
qc2.h(q2[0])
qc.measure(q[0],c2[0])
qc2.h(q[0])

print('__________qc______________')
qasm = qasm2.dumps(qc)
print(qasm[36:len(qasm)])

print('__________qc2______________')
qasm = qasm2.dumps(qc2)
print(qasm[36:len(qasm)])

q1 = QuantumRegister(2, name='q1')
c1 = ClassicalRegister(3, name='c1')
qc1 = QuantumCircuit(q1, c1,name='qc1')

q2 = QuantumRegister(2, name='q2')
c2 = ClassicalRegister(3, name='c2')
qc2 = QuantumCircuit(q2, c2,name='qc2')

qc1.h(q1[0])
qc1.id(q1[1])

qc2.id(q2[0])
qc2.h(q2[1])

qc3 = qc1 & qc2
print('__________qc3 = qc1 & qc2______________')
qasm = qasm2.dumps(qc3)
print(qasm[36:len(qasm)])

qc1 &= qc1
print('__________qc1 &= qc2______________')
qasm = qasm2.dumps(qc1)
print(qasm[36:len(qasm)])


q = QuantumRegister(2, name='q')
c = ClassicalRegister(2, name='c')
qc = QuantumCircuit(q, c,name='qc')

qc.h(q[0])
qc.h(q[1])
qc.measure(q,c)

print(qc)
qc.draw(output='mpl')

